refactor(api-types): strip comments, extract DEFAULT_ACCESS_MODE constant

This commit is contained in:
2026-07-12 04:12:51 +02:00
parent 9dcd169689
commit eff14228af
10 changed files with 7 additions and 103 deletions

View File

@@ -1,17 +1,13 @@
//! Admin response DTOs.
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utoipa::ToSchema; use utoipa::ToSchema;
use uuid::Uuid; use uuid::Uuid;
/// Admin settings response (key-value pairs from `app_settings` table).
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SettingsResponse { pub struct SettingsResponse {
pub settings: std::collections::HashMap<String, String>, pub settings: std::collections::HashMap<String, String>,
} }
/// An activity log entry for the admin dashboard.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ActivityEventResponse { pub struct ActivityEventResponse {
pub id: Uuid, pub id: Uuid,

View File

@@ -1,11 +1,8 @@
//! Authentication request and response DTOs.
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utoipa::ToSchema; use utoipa::ToSchema;
use uuid::Uuid; use uuid::Uuid;
/// Login request.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct LoginRequest { pub struct LoginRequest {
pub email: String, pub email: String,
@@ -14,31 +11,26 @@ pub struct LoginRequest {
pub remember_me: bool, pub remember_me: bool,
} }
/// Register request.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct RegisterRequest { pub struct RegisterRequest {
pub email: String, pub email: String,
pub password: String, pub password: String,
} }
/// Refresh token request.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct RefreshRequest { pub struct RefreshRequest {
pub refresh_token: String, pub refresh_token: String,
} }
/// JWT token response.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct TokenResponse { pub struct TokenResponse {
pub access_token: String, pub access_token: String,
pub token_type: String, pub token_type: String,
pub expires_in: u64, pub expires_in: u64,
/// Only present when `remember_me` was true at login, or on token refresh.
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub refresh_token: Option<String>, pub refresh_token: Option<String>,
} }
/// User response DTO.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct UserResponse { pub struct UserResponse {
pub id: Uuid, pub id: Uuid,

View File

@@ -1,5 +1,3 @@
//! Channel request and response DTOs.
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utoipa::ToSchema; use utoipa::ToSchema;
@@ -7,16 +5,12 @@ use uuid::Uuid;
use crate::common::enum_to_string; use crate::common::enum_to_string;
/// Create channel request.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct CreateChannelRequest { pub struct CreateChannelRequest {
pub name: String, pub name: String,
pub description: Option<String>, pub description: Option<String>,
/// IANA timezone, e.g. "UTC" or "America/New_York".
pub timezone: String, pub timezone: String,
/// One of: "public", "password_protected", "account_required", "owner_only".
pub access_mode: Option<String>, pub access_mode: Option<String>,
/// Plain-text password; hashed before storage.
pub access_password: Option<String>, pub access_password: Option<String>,
pub webhook_url: Option<String>, pub webhook_url: Option<String>,
pub webhook_poll_interval_secs: Option<u32>, pub webhook_poll_interval_secs: Option<u32>,
@@ -24,36 +18,25 @@ pub struct CreateChannelRequest {
pub webhook_headers: Option<String>, pub webhook_headers: Option<String>,
} }
/// Update channel request. All fields are optional -- only provided fields are
/// updated.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct UpdateChannelRequest { pub struct UpdateChannelRequest {
pub name: Option<String>, pub name: Option<String>,
pub description: Option<String>, pub description: Option<String>,
pub timezone: Option<String>, pub timezone: Option<String>,
/// Replace the entire schedule config (template import/edit).
pub schedule_config: Option<serde_json::Value>, pub schedule_config: Option<serde_json::Value>,
pub recycle_policy: Option<serde_json::Value>, pub recycle_policy: Option<serde_json::Value>,
pub auto_schedule: Option<bool>, pub auto_schedule: Option<bool>,
/// One of: "public", "password_protected", "account_required", "owner_only".
pub access_mode: Option<String>, pub access_mode: Option<String>,
/// Empty string clears the password; non-empty re-hashes.
pub access_password: Option<String>, pub access_password: Option<String>,
/// `null` = clear logo, string = set logo URL. Omit to leave unchanged.
pub logo: Option<Option<String>>, pub logo: Option<Option<String>>,
/// One of: "top_left", "top_right", "bottom_left", "bottom_right".
pub logo_position: Option<String>, pub logo_position: Option<String>,
pub logo_opacity: Option<f32>, pub logo_opacity: Option<f32>,
/// `null` = clear, string = set. Omit to leave unchanged.
pub webhook_url: Option<Option<String>>, pub webhook_url: Option<Option<String>>,
pub webhook_poll_interval_secs: Option<u32>, pub webhook_poll_interval_secs: Option<u32>,
/// `null` = clear, string = set. Omit to leave unchanged.
pub webhook_body_template: Option<Option<String>>, pub webhook_body_template: Option<Option<String>>,
/// `null` = clear, string = set. Omit to leave unchanged.
pub webhook_headers: Option<Option<String>>, pub webhook_headers: Option<Option<String>>,
} }
/// Channel response DTO.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ChannelResponse { pub struct ChannelResponse {
pub id: Uuid, pub id: Uuid,
@@ -61,15 +44,11 @@ pub struct ChannelResponse {
pub name: String, pub name: String,
pub description: Option<String>, pub description: Option<String>,
pub timezone: String, pub timezone: String,
/// The full schedule config as a JSON object, decoupled from domain internals.
pub schedule_config: serde_json::Value, pub schedule_config: serde_json::Value,
/// The recycle policy as a JSON object.
pub recycle_policy: serde_json::Value, pub recycle_policy: serde_json::Value,
pub auto_schedule: bool, pub auto_schedule: bool,
/// E.g. "public", "password_protected", "account_required", "owner_only".
pub access_mode: String, pub access_mode: String,
pub logo: Option<String>, pub logo: Option<String>,
/// E.g. "top_left", "top_right", "bottom_left", "bottom_right".
pub logo_position: String, pub logo_position: String,
pub logo_opacity: f32, pub logo_opacity: f32,
pub webhook_url: Option<String>, pub webhook_url: Option<String>,
@@ -105,7 +84,6 @@ impl From<domain::Channel> for ChannelResponse {
} }
} }
/// Config history snapshot response.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ConfigSnapshotResponse { pub struct ConfigSnapshotResponse {
pub id: Uuid, pub id: Uuid,
@@ -125,7 +103,6 @@ impl From<domain::ChannelConfigSnapshot> for ConfigSnapshotResponse {
} }
} }
/// Patch snapshot request (rename label).
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PatchSnapshotRequest { pub struct PatchSnapshotRequest {
pub label: Option<String>, pub label: Option<String>,

View File

@@ -1,9 +1,6 @@
//! Common types shared across API modules.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utoipa::ToSchema; use utoipa::ToSchema;
/// Paginated response wrapper.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct PaginatedResponse<T: ToSchema> { pub struct PaginatedResponse<T: ToSchema> {
pub items: Vec<T>, pub items: Vec<T>,
@@ -16,7 +13,6 @@ impl<T: ToSchema> PaginatedResponse<T> {
} }
} }
/// Standard error response body.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ErrorResponse { pub struct ErrorResponse {
pub error: String, pub error: String,
@@ -40,10 +36,6 @@ impl ErrorResponse {
} }
} }
/// Serialize a `serde::Serialize` enum to its snake_case string representation.
///
/// Used internally by `From` impls to convert domain enums (AccessMode,
/// LogoPosition, ContentType, etc.) into plain strings for API responses.
pub(crate) fn enum_to_string<T: Serialize>(val: &T) -> String { pub(crate) fn enum_to_string<T: Serialize>(val: &T) -> String {
serde_json::to_value(val) serde_json::to_value(val)
.ok() .ok()

View File

@@ -1,11 +1,8 @@
//! System configuration response DTOs.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utoipa::ToSchema; use utoipa::ToSchema;
use crate::common::enum_to_string; use crate::common::enum_to_string;
/// Provider capabilities response, mirroring the domain type for OpenAPI docs.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ProviderCapabilitiesResponse { pub struct ProviderCapabilitiesResponse {
pub collections: bool, pub collections: bool,
@@ -14,11 +11,8 @@ pub struct ProviderCapabilitiesResponse {
pub tags: bool, pub tags: bool,
pub decade: bool, pub decade: bool,
pub search: bool, pub search: bool,
/// E.g. "hls" or "direct_file".
pub streaming_protocol: String, pub streaming_protocol: String,
/// Whether `POST /files/rescan` is available.
pub rescan: bool, pub rescan: bool,
/// Whether on-demand FFmpeg transcoding to HLS is available.
pub transcode: bool, pub transcode: bool,
} }
@@ -38,21 +32,16 @@ impl From<domain::ports::ProviderCapabilities> for ProviderCapabilitiesResponse
} }
} }
/// Per-provider info returned in the system config response.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ProviderInfo { pub struct ProviderInfo {
pub id: String, pub id: String,
pub capabilities: ProviderCapabilitiesResponse, pub capabilities: ProviderCapabilitiesResponse,
} }
/// System configuration response (`GET /config`).
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ConfigResponse { pub struct ConfigResponse {
pub allow_registration: bool, pub allow_registration: bool,
/// All registered providers with their capabilities.
pub providers: Vec<ProviderInfo>, pub providers: Vec<ProviderInfo>,
/// Capabilities of the primary provider -- kept for backward compatibility.
pub provider_capabilities: ProviderCapabilitiesResponse, pub provider_capabilities: ProviderCapabilitiesResponse,
/// Provider type strings supported by this build (feature-gated).
pub available_provider_types: Vec<String>, pub available_provider_types: Vec<String>,
} }

View File

@@ -1,12 +1,3 @@
//! HTTP request and response DTOs with OpenAPI schema generation.
//!
//! Pure data transfer objects for the API layer. All structs derive
//! `Serialize`, `Deserialize`, and `utoipa::ToSchema` for automatic
//! OpenAPI documentation.
//!
//! Response types provide `From<DomainType>` implementations to
//! convert domain models into API-facing DTOs.
pub mod admin; pub mod admin;
pub mod auth; pub mod auth;
pub mod channels; pub mod channels;
@@ -17,7 +8,6 @@ pub mod providers;
pub mod schedule; pub mod schedule;
pub mod transcode; pub mod transcode;
// Re-export all public types for convenience.
pub use admin::{ActivityEventResponse, SettingsResponse}; pub use admin::{ActivityEventResponse, SettingsResponse};
pub use auth::{LoginRequest, RefreshRequest, RegisterRequest, TokenResponse, UserResponse}; pub use auth::{LoginRequest, RefreshRequest, RegisterRequest, TokenResponse, UserResponse};
pub use channels::{ pub use channels::{
@@ -32,4 +22,6 @@ pub use schedule::{
CurrentBroadcastResponse, MediaItemResponse, ScheduleHistoryEntry, ScheduleResponse, CurrentBroadcastResponse, MediaItemResponse, ScheduleHistoryEntry, ScheduleResponse,
SlotResponse, SlotResponse,
}; };
pub use transcode::{TranscodeSettingsResponse, TranscodeStatsResponse, UpdateTranscodeSettingsRequest}; pub use transcode::{
TranscodeSettingsResponse, TranscodeStatsResponse, UpdateTranscodeSettingsRequest,
};

View File

@@ -1,18 +1,14 @@
//! Library browsing response DTOs.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utoipa::ToSchema; use utoipa::ToSchema;
use crate::common::enum_to_string; use crate::common::enum_to_string;
/// Library item response (synced from a media provider).
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct LibraryItemResponse { pub struct LibraryItemResponse {
pub id: String, pub id: String,
pub provider_id: String, pub provider_id: String,
pub external_id: String, pub external_id: String,
pub title: String, pub title: String,
/// E.g. "movie", "episode", "short".
pub content_type: String, pub content_type: String,
pub duration_secs: u32, pub duration_secs: u32,
pub series_name: Option<String>, pub series_name: Option<String>,
@@ -52,7 +48,6 @@ impl From<domain::LibraryItem> for LibraryItemResponse {
} }
} }
/// Library collection summary.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct CollectionResponse { pub struct CollectionResponse {
pub id: String, pub id: String,
@@ -70,7 +65,6 @@ impl From<domain::LibraryCollection> for CollectionResponse {
} }
} }
/// TV show summary aggregated from synced episodes.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ShowResponse { pub struct ShowResponse {
pub series_name: String, pub series_name: String,
@@ -92,7 +86,6 @@ impl From<domain::ShowSummary> for ShowResponse {
} }
} }
/// Season summary within a TV show.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SeasonResponse { pub struct SeasonResponse {
pub season_number: u32, pub season_number: u32,

View File

@@ -1,14 +1,9 @@
//! Provider configuration request and response DTOs.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utoipa::ToSchema; use utoipa::ToSchema;
/// Request to create or update a provider configuration.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ProviderConfigRequest { pub struct ProviderConfigRequest {
/// E.g. "jellyfin", "local_files".
pub provider_type: String, pub provider_type: String,
/// Provider-specific configuration blob (URL, API key, path, etc.).
pub config: serde_json::Value, pub config: serde_json::Value,
#[serde(default = "default_true")] #[serde(default = "default_true")]
pub enabled: bool, pub enabled: bool,
@@ -18,12 +13,10 @@ fn default_true() -> bool {
true true
} }
/// Provider configuration response.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ProviderConfigResponse { pub struct ProviderConfigResponse {
pub id: String, pub id: String,
pub provider_type: String, pub provider_type: String,
/// Provider-specific configuration blob (deserialized from stored JSON).
pub config: serde_json::Value, pub config: serde_json::Value,
pub enabled: bool, pub enabled: bool,
pub updated_at: String, pub updated_at: String,

View File

@@ -1,5 +1,3 @@
//! Schedule and EPG response DTOs.
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utoipa::ToSchema; use utoipa::ToSchema;
@@ -7,12 +5,12 @@ use uuid::Uuid;
use crate::common::enum_to_string; use crate::common::enum_to_string;
/// Media item snapshot within a scheduled slot. const DEFAULT_ACCESS_MODE: &str = "public";
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct MediaItemResponse { pub struct MediaItemResponse {
pub id: String, pub id: String,
pub title: String, pub title: String,
/// E.g. "movie", "episode", "short".
pub content_type: String, pub content_type: String,
pub duration_secs: u32, pub duration_secs: u32,
pub description: Option<String>, pub description: Option<String>,
@@ -42,7 +40,6 @@ impl From<domain::MediaItem> for MediaItemResponse {
} }
} }
/// A single resolved broadcast slot within a schedule.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct SlotResponse { pub struct SlotResponse {
pub id: Uuid, pub id: Uuid,
@@ -50,7 +47,6 @@ pub struct SlotResponse {
pub end_at: DateTime<Utc>, pub end_at: DateTime<Utc>,
pub item: MediaItemResponse, pub item: MediaItemResponse,
pub source_block_id: Uuid, pub source_block_id: Uuid,
/// Access mode of the programming block that produced this slot.
#[serde(default)] #[serde(default)]
pub block_access_mode: String, pub block_access_mode: String,
} }
@@ -63,21 +59,19 @@ impl From<domain::ScheduledSlot> for SlotResponse {
end_at: s.end_at(), end_at: s.end_at(),
item: s.item().clone().into(), item: s.item().clone().into(),
source_block_id: s.source_block_id().value(), source_block_id: s.source_block_id().value(),
block_access_mode: String::from("public"), block_access_mode: String::from(DEFAULT_ACCESS_MODE),
} }
} }
} }
impl SlotResponse { impl SlotResponse {
/// Build a slot response with the block-level access mode resolved from the
/// channel's schedule config.
pub fn with_block_access(slot: domain::ScheduledSlot, channel: &domain::Channel) -> Self { pub fn with_block_access(slot: domain::ScheduledSlot, channel: &domain::Channel) -> Self {
let block_access_mode = channel let block_access_mode = channel
.schedule_config() .schedule_config()
.all_blocks() .all_blocks()
.find(|b| b.id() == slot.source_block_id()) .find(|b| b.id() == slot.source_block_id())
.map(|b| enum_to_string(b.access_mode())) .map(|b| enum_to_string(b.access_mode()))
.unwrap_or_else(|| String::from("public")); .unwrap_or_else(|| String::from(DEFAULT_ACCESS_MODE));
Self { Self {
id: slot.id().value(), id: slot.id().value(),
start_at: slot.start_at(), start_at: slot.start_at(),
@@ -89,21 +83,13 @@ impl SlotResponse {
} }
} }
/// What is currently playing on a channel.
///
/// A 204 No Content response is returned instead when there is no active slot
/// (no-signal / dead air).
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct CurrentBroadcastResponse { pub struct CurrentBroadcastResponse {
pub slot: SlotResponse, pub slot: SlotResponse,
/// Seconds elapsed since the start of the current item -- use as the
/// initial seek position for the player.
pub offset_secs: u32, pub offset_secs: u32,
/// Access mode of the block currently playing. The stream is gated by this.
pub block_access_mode: String, pub block_access_mode: String,
} }
/// Full schedule response with all resolved slots.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ScheduleResponse { pub struct ScheduleResponse {
pub id: Uuid, pub id: Uuid,
@@ -133,7 +119,6 @@ impl From<domain::GeneratedSchedule> for ScheduleResponse {
} }
} }
/// Compact schedule history entry (no slots, just metadata).
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ScheduleHistoryEntry { pub struct ScheduleHistoryEntry {
pub id: Uuid, pub id: Uuid,

View File

@@ -1,21 +1,16 @@
//! Transcode settings and stats response DTOs.
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utoipa::ToSchema; use utoipa::ToSchema;
/// Transcode settings response.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct TranscodeSettingsResponse { pub struct TranscodeSettingsResponse {
pub cleanup_ttl_hours: u32, pub cleanup_ttl_hours: u32,
} }
/// Request to update transcode settings.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct UpdateTranscodeSettingsRequest { pub struct UpdateTranscodeSettingsRequest {
pub cleanup_ttl_hours: u32, pub cleanup_ttl_hours: u32,
} }
/// Transcode cache statistics.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct TranscodeStatsResponse { pub struct TranscodeStatsResponse {
pub cache_size_bytes: u64, pub cache_size_bytes: u64,