refactor: fix HIGH+MEDIUM architectural violations from code review
HIGH: fix watch_medium data-loss bug, standardize error handling on ApiError, fix dep direction (rss/template-askama no longer dep on application), extract ImageFetcher port (remove reqwest from app layer), move event construction from save_review to use case, extract infra-wiring crate (DbPool/EventBusBackend dedup), deduplicate presentation helpers (encode_error, export streaming, multipart parsing) MEDIUM: split LocalApContentQuery god-trait 10→3 methods, dedup movie resolution orchestration, add RemoteActorDto/PersonDto mappers, move AppConfig to infra-wiring, fix SocialQueryPort Uuid→UserId, replace stringly-typed api-types with domain enums, move count_reviews_in_year to StatsRepository, dedup event publisher cfg blocks, extract should_enrich, move group_by_month to application, dedup count_local_posts, add FederationFlags Default, TUI input helper + ShowError rename + typed auth errors, api-types cleanup (UserSettingsDto/UserProfileBase/PreviewRowData) 102 files changed, -681 lines net
This commit is contained in:
@@ -7,3 +7,4 @@ edition = "2024"
|
||||
serde = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
utoipa = { version = "5.5.0", features = ["axum_extras", "uuid"] }
|
||||
domain = { path = "../domain" }
|
||||
|
||||
@@ -14,7 +14,8 @@ pub struct LoginResponse {
|
||||
pub user_id: Uuid,
|
||||
pub email: String,
|
||||
pub expires_at: String,
|
||||
pub role: String,
|
||||
#[schema(value_type = String)]
|
||||
pub role: domain::models::UserRole,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
|
||||
@@ -18,7 +18,8 @@ pub struct LogReviewRequest {
|
||||
pub comment: Option<String>,
|
||||
pub watched_at: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub watch_medium: Option<String>,
|
||||
#[schema(value_type = Option<String>)]
|
||||
pub watch_medium: Option<domain::value_objects::WatchMedium>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
@@ -89,7 +90,8 @@ pub struct EditReviewRequest {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub watched_at: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub watch_medium: Option<Option<String>>,
|
||||
#[schema(value_type = Option<Option<String>>)]
|
||||
pub watch_medium: Option<Option<domain::value_objects::WatchMedium>>,
|
||||
}
|
||||
|
||||
fn default_export_format() -> String {
|
||||
|
||||
@@ -7,7 +7,8 @@ pub struct GoalDto {
|
||||
pub current_count: u32,
|
||||
pub percentage: f64,
|
||||
pub is_complete: bool,
|
||||
pub goal_type: String,
|
||||
#[schema(value_type = String)]
|
||||
pub goal_type: domain::models::GoalType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
@@ -25,17 +26,3 @@ pub struct CreateGoalRequest {
|
||||
pub struct UpdateGoalRequest {
|
||||
pub target_count: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UserSettingsDto {
|
||||
pub federate_goals: bool,
|
||||
pub federate_reviews: bool,
|
||||
pub federate_watchlist: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UpdateUserSettingsRequest {
|
||||
pub federate_goals: bool,
|
||||
pub federate_reviews: bool,
|
||||
pub federate_watchlist: bool,
|
||||
}
|
||||
|
||||
@@ -46,29 +46,24 @@ pub struct SaveProfileRequest {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct PreviewRowData {
|
||||
pub index: usize,
|
||||
pub title: Option<String>,
|
||||
pub release_year: Option<String>,
|
||||
pub director: Option<String>,
|
||||
pub rating: Option<String>,
|
||||
pub watched_at: Option<String>,
|
||||
pub comment: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
#[serde(tag = "status")]
|
||||
pub enum PreviewRowDto {
|
||||
#[serde(rename = "valid")]
|
||||
Valid {
|
||||
index: usize,
|
||||
title: Option<String>,
|
||||
release_year: Option<String>,
|
||||
director: Option<String>,
|
||||
rating: Option<String>,
|
||||
watched_at: Option<String>,
|
||||
comment: Option<String>,
|
||||
},
|
||||
Valid(PreviewRowData),
|
||||
#[serde(rename = "duplicate")]
|
||||
Duplicate {
|
||||
index: usize,
|
||||
title: Option<String>,
|
||||
release_year: Option<String>,
|
||||
director: Option<String>,
|
||||
rating: Option<String>,
|
||||
watched_at: Option<String>,
|
||||
comment: Option<String>,
|
||||
},
|
||||
Duplicate(PreviewRowData),
|
||||
#[serde(rename = "invalid")]
|
||||
Invalid { index: usize, errors: Vec<String> },
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ pub mod diary;
|
||||
pub mod goals;
|
||||
pub mod import;
|
||||
pub mod movies;
|
||||
pub mod rendering;
|
||||
pub mod search;
|
||||
pub mod social;
|
||||
pub mod users;
|
||||
@@ -17,6 +18,7 @@ pub use diary::*;
|
||||
pub use goals::*;
|
||||
pub use import::*;
|
||||
pub use movies::*;
|
||||
pub use rendering::*;
|
||||
pub use social::*;
|
||||
pub use users::*;
|
||||
pub use watchlist::*;
|
||||
|
||||
@@ -99,7 +99,8 @@ pub struct ReviewDto {
|
||||
pub comment: Option<String>,
|
||||
pub watched_at: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub watch_medium: Option<String>,
|
||||
#[schema(value_type = Option<String>)]
|
||||
pub watch_medium: Option<domain::value_objects::WatchMedium>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
@@ -125,7 +126,8 @@ pub struct SocialReviewDto {
|
||||
pub watched_at: String,
|
||||
pub is_federated: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub watch_medium: Option<String>,
|
||||
#[schema(value_type = Option<String>)]
|
||||
pub watch_medium: Option<domain::value_objects::WatchMedium>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
|
||||
19
crates/api-types/src/rendering.rs
Normal file
19
crates/api-types/src/rendering.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct HtmlPageContext {
|
||||
pub user_email: Option<String>,
|
||||
pub user_id: Option<Uuid>,
|
||||
pub is_admin: bool,
|
||||
pub register_enabled: bool,
|
||||
pub rss_url: String,
|
||||
pub page_title: String,
|
||||
pub canonical_url: String,
|
||||
pub csrf_token: String,
|
||||
pub page_rss_url: Option<String>,
|
||||
}
|
||||
|
||||
impl HtmlPageContext {
|
||||
pub fn is_current_user(&self, id: Uuid) -> bool {
|
||||
self.user_id == Some(id)
|
||||
}
|
||||
}
|
||||
@@ -67,13 +67,23 @@ pub struct UserTrendsDto {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UserProfileResponse {
|
||||
pub user_id: Uuid,
|
||||
pub struct UserProfileBase {
|
||||
pub username: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub display_name: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bio: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub avatar_url: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub banner_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UserProfileResponse {
|
||||
pub user_id: Uuid,
|
||||
#[serde(flatten)]
|
||||
pub profile: UserProfileBase,
|
||||
pub stats: UserStatsDto,
|
||||
pub following_count: usize,
|
||||
pub followers_count: usize,
|
||||
@@ -90,23 +100,17 @@ pub struct UserProfileResponse {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub handle: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub display_name: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bio: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub actor_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ProfileResponse {
|
||||
pub username: String,
|
||||
pub display_name: Option<String>,
|
||||
pub bio: Option<String>,
|
||||
pub avatar_url: Option<String>,
|
||||
pub banner_url: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub profile: UserProfileBase,
|
||||
pub also_known_as: Option<String>,
|
||||
pub fields: Vec<ProfileFieldDto>,
|
||||
pub role: String,
|
||||
#[schema(value_type = String)]
|
||||
pub role: domain::models::UserRole,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
@@ -119,3 +123,17 @@ pub struct ProfileFieldDto {
|
||||
pub struct UpdateProfileFieldsRequest {
|
||||
pub fields: Vec<ProfileFieldDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UserSettingsDto {
|
||||
pub federate_goals: bool,
|
||||
pub federate_reviews: bool,
|
||||
pub federate_watchlist: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UpdateUserSettingsRequest {
|
||||
pub federate_goals: bool,
|
||||
pub federate_reviews: bool,
|
||||
pub federate_watchlist: bool,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user