refactor: deps cleanup, split openapi, extract api-types crate
This commit is contained in:
9
crates/api-types/Cargo.toml
Normal file
9
crates/api-types/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "api-types"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
utoipa = { version = "5.5.0", features = ["axum_extras", "uuid"] }
|
||||
23
crates/api-types/src/auth.rs
Normal file
23
crates/api-types/src/auth.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct LoginRequest {
|
||||
pub email: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct LoginResponse {
|
||||
pub token: String,
|
||||
pub user_id: Uuid,
|
||||
pub email: String,
|
||||
pub expires_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct RegisterRequest {
|
||||
pub email: String,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
7
crates/api-types/src/common.rs
Normal file
7
crates/api-types/src/common.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Default)]
|
||||
pub struct PaginationQueryParams {
|
||||
pub limit: Option<u32>,
|
||||
pub offset: Option<u32>,
|
||||
}
|
||||
78
crates/api-types/src/diary.rs
Normal file
78
crates/api-types/src/diary.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::movies::{MovieDto, ReviewDto};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct LogReviewRequest {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub external_metadata_id: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub manual_title: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub manual_release_year: Option<u16>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub manual_director: Option<String>,
|
||||
pub rating: u8,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub comment: Option<String>,
|
||||
pub watched_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct DiaryEntryDto {
|
||||
pub movie: MovieDto,
|
||||
pub review: ReviewDto,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct DiaryResponse {
|
||||
pub items: Vec<DiaryEntryDto>,
|
||||
pub total_count: u64,
|
||||
pub limit: u32,
|
||||
pub offset: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, utoipa::IntoParams)]
|
||||
#[into_params(parameter_in = Query)]
|
||||
pub struct DiaryQueryParams {
|
||||
pub limit: Option<u32>,
|
||||
pub offset: Option<u32>,
|
||||
pub sort_by: Option<String>,
|
||||
pub movie_id: Option<Uuid>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, utoipa::IntoParams)]
|
||||
#[into_params(parameter_in = Query)]
|
||||
pub struct ActivityFeedQueryParams {
|
||||
pub limit: Option<u32>,
|
||||
pub offset: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct FeedEntryDto {
|
||||
pub movie: MovieDto,
|
||||
pub review: ReviewDto,
|
||||
pub user_email: String,
|
||||
pub user_display_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ActivityFeedResponse {
|
||||
pub items: Vec<FeedEntryDto>,
|
||||
pub total_count: u64,
|
||||
pub limit: u32,
|
||||
pub offset: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, utoipa::IntoParams)]
|
||||
#[into_params(parameter_in = Query)]
|
||||
pub struct ExportQueryParams {
|
||||
/// Output format: `csv` (default) or `json`
|
||||
#[serde(default = "default_export_format")]
|
||||
pub format: String,
|
||||
}
|
||||
|
||||
fn default_export_format() -> String {
|
||||
"csv".to_string()
|
||||
}
|
||||
47
crates/api-types/src/import.rs
Normal file
47
crates/api-types/src/import.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct SessionCreatedResponse {
|
||||
pub session_id: String,
|
||||
pub columns: Vec<String>,
|
||||
pub sample_rows: Vec<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct SessionStateResponse {
|
||||
pub session_id: String,
|
||||
pub columns: Vec<String>,
|
||||
pub has_mappings: bool,
|
||||
pub row_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ApiFieldMapping {
|
||||
/// Column name in the source file
|
||||
pub source_column: String,
|
||||
/// Domain field: title | release_year | director | rating | watched_at | comment | external_metadata_id
|
||||
pub domain_field: String,
|
||||
/// For rating fields: multiply raw value by this factor (e.g. 0.5 for 10-point → 5-point scale)
|
||||
pub rating_scale: Option<f64>,
|
||||
/// For watched_at fields: strftime format hint (e.g. "%d/%m/%Y")
|
||||
pub date_format: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ApplyMappingRequest {
|
||||
pub mappings: Vec<ApiFieldMapping>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ConfirmRequest {
|
||||
/// Indices (0-based) of rows from the mapping preview to import
|
||||
pub confirmed_indices: Vec<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct SaveProfileRequest {
|
||||
/// Session UUID whose current field_mappings to save
|
||||
pub session_id: String,
|
||||
/// Human-readable profile name (e.g. "Letterboxd")
|
||||
pub name: String,
|
||||
}
|
||||
15
crates/api-types/src/lib.rs
Normal file
15
crates/api-types/src/lib.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
pub mod auth;
|
||||
pub mod common;
|
||||
pub mod diary;
|
||||
pub mod import;
|
||||
pub mod movies;
|
||||
pub mod social;
|
||||
pub mod users;
|
||||
|
||||
pub use auth::*;
|
||||
pub use common::*;
|
||||
pub use diary::*;
|
||||
pub use import::*;
|
||||
pub use movies::*;
|
||||
pub use social::*;
|
||||
pub use users::*;
|
||||
58
crates/api-types/src/movies.rs
Normal file
58
crates/api-types/src/movies.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct MovieDto {
|
||||
pub id: Uuid,
|
||||
pub title: String,
|
||||
pub release_year: u16,
|
||||
pub director: Option<String>,
|
||||
pub poster_path: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ReviewDto {
|
||||
pub id: Uuid,
|
||||
pub rating: u8,
|
||||
pub comment: Option<String>,
|
||||
pub watched_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ReviewHistoryResponse {
|
||||
pub movie: MovieDto,
|
||||
pub viewings: Vec<ReviewDto>,
|
||||
pub trend: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct MovieStatsDto {
|
||||
pub total_count: u64,
|
||||
pub avg_rating: Option<f64>,
|
||||
pub federated_count: u64,
|
||||
pub rating_histogram: [u64; 5],
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct SocialReviewDto {
|
||||
pub user_display: String,
|
||||
pub rating: u8,
|
||||
pub comment: Option<String>,
|
||||
pub watched_at: String,
|
||||
pub is_federated: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct SocialFeedResponse {
|
||||
pub items: Vec<SocialReviewDto>,
|
||||
pub total_count: u64,
|
||||
pub limit: u32,
|
||||
pub offset: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct MovieDetailResponse {
|
||||
pub movie: MovieDto,
|
||||
pub stats: MovieStatsDto,
|
||||
pub reviews: SocialFeedResponse,
|
||||
}
|
||||
44
crates/api-types/src/social.rs
Normal file
44
crates/api-types/src/social.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct FollowRequest {
|
||||
pub handle: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ActorUrlRequest {
|
||||
pub actor_url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct RemoteActorDto {
|
||||
pub handle: String,
|
||||
pub display_name: Option<String>,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ActorListResponse {
|
||||
pub actors: Vec<RemoteActorDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct BlockedDomainResponse {
|
||||
pub domain: String,
|
||||
pub reason: Option<String>,
|
||||
pub blocked_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct AddBlockedDomainRequest {
|
||||
pub domain: String,
|
||||
pub reason: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct BlockedActorResponse {
|
||||
pub url: String,
|
||||
pub handle: String,
|
||||
pub display_name: Option<String>,
|
||||
pub avatar_url: Option<String>,
|
||||
}
|
||||
85
crates/api-types/src/users.rs
Normal file
85
crates/api-types/src/users.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::diary::{DiaryEntryDto, DiaryResponse};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UserSummaryDto {
|
||||
pub id: Uuid,
|
||||
pub email: String,
|
||||
pub total_movies: i64,
|
||||
pub avg_rating: Option<f64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UsersResponse {
|
||||
pub users: Vec<UserSummaryDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, utoipa::IntoParams)]
|
||||
#[into_params(parameter_in = Query)]
|
||||
pub struct UserProfileQueryParams {
|
||||
/// One of: `recent` (default), `ratings`, `history`, `trends`
|
||||
pub view: Option<String>,
|
||||
pub limit: Option<u32>,
|
||||
pub offset: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UserStatsDto {
|
||||
pub total_movies: i64,
|
||||
pub avg_rating: Option<f64>,
|
||||
pub favorite_director: Option<String>,
|
||||
pub most_active_month: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct MonthActivityDto {
|
||||
pub year_month: String,
|
||||
pub month_label: String,
|
||||
pub count: i64,
|
||||
pub entries: Vec<DiaryEntryDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct MonthlyRatingDto {
|
||||
pub year_month: String,
|
||||
pub month_label: String,
|
||||
pub avg_rating: f64,
|
||||
pub count: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct DirectorStatDto {
|
||||
pub director: String,
|
||||
pub count: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UserTrendsDto {
|
||||
pub monthly_ratings: Vec<MonthlyRatingDto>,
|
||||
pub top_directors: Vec<DirectorStatDto>,
|
||||
pub max_director_count: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct UserProfileResponse {
|
||||
pub user_id: Uuid,
|
||||
pub username: String,
|
||||
pub stats: UserStatsDto,
|
||||
pub following_count: usize,
|
||||
pub followers_count: usize,
|
||||
/// Populated for view=recent and view=ratings
|
||||
pub entries: Option<DiaryResponse>,
|
||||
/// Populated for view=history
|
||||
pub history: Option<Vec<MonthActivityDto>>,
|
||||
/// Populated for view=trends
|
||||
pub trends: Option<UserTrendsDto>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
|
||||
pub struct ProfileResponse {
|
||||
pub username: String,
|
||||
pub bio: Option<String>,
|
||||
pub avatar_url: Option<String>,
|
||||
}
|
||||
Reference in New Issue
Block a user