feat: add access control to channels with various modes
- Introduced AccessMode enum to define channel access levels: Public, PasswordProtected, AccountRequired, and OwnerOnly. - Updated Channel and ProgrammingBlock entities to include access_mode and access_password_hash fields. - Enhanced create and update channel functionality to handle access mode and password. - Implemented access checks in channel routes based on the defined access modes. - Modified frontend components to support channel creation and editing with access control options. - Added ChannelPasswordModal for handling password input when accessing restricted channels. - Updated API calls to include channel and block passwords as needed. - Created database migrations to add access_mode and access_password_hash columns to channels table.
This commit is contained in:
@@ -58,6 +58,9 @@ pub struct CreateChannelRequest {
|
||||
pub description: Option<String>,
|
||||
/// IANA timezone, e.g. "UTC" or "America/New_York"
|
||||
pub timezone: String,
|
||||
pub access_mode: Option<domain::AccessMode>,
|
||||
/// Plain-text password; hashed before storage.
|
||||
pub access_password: Option<String>,
|
||||
}
|
||||
|
||||
/// All fields are optional — only provided fields are updated.
|
||||
@@ -70,6 +73,9 @@ pub struct UpdateChannelRequest {
|
||||
pub schedule_config: Option<domain::ScheduleConfig>,
|
||||
pub recycle_policy: Option<domain::RecyclePolicy>,
|
||||
pub auto_schedule: Option<bool>,
|
||||
pub access_mode: Option<domain::AccessMode>,
|
||||
/// Empty string clears the password; non-empty re-hashes.
|
||||
pub access_password: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@@ -82,6 +88,7 @@ pub struct ChannelResponse {
|
||||
pub schedule_config: domain::ScheduleConfig,
|
||||
pub recycle_policy: domain::RecyclePolicy,
|
||||
pub auto_schedule: bool,
|
||||
pub access_mode: domain::AccessMode,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
@@ -97,6 +104,7 @@ impl From<domain::Channel> for ChannelResponse {
|
||||
schedule_config: c.schedule_config,
|
||||
recycle_policy: c.recycle_policy,
|
||||
auto_schedule: c.auto_schedule,
|
||||
access_mode: c.access_mode,
|
||||
created_at: c.created_at,
|
||||
updated_at: c.updated_at,
|
||||
}
|
||||
@@ -147,6 +155,8 @@ pub struct ScheduledSlotResponse {
|
||||
pub end_at: DateTime<Utc>,
|
||||
pub item: MediaItemResponse,
|
||||
pub source_block_id: Uuid,
|
||||
#[serde(default)]
|
||||
pub block_access_mode: domain::AccessMode,
|
||||
}
|
||||
|
||||
impl From<domain::ScheduledSlot> for ScheduledSlotResponse {
|
||||
@@ -157,6 +167,27 @@ impl From<domain::ScheduledSlot> for ScheduledSlotResponse {
|
||||
end_at: s.end_at,
|
||||
item: s.item.into(),
|
||||
source_block_id: s.source_block_id,
|
||||
block_access_mode: domain::AccessMode::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScheduledSlotResponse {
|
||||
pub fn with_block_access(slot: domain::ScheduledSlot, channel: &domain::Channel) -> Self {
|
||||
let block_access_mode = channel
|
||||
.schedule_config
|
||||
.blocks
|
||||
.iter()
|
||||
.find(|b| b.id == slot.source_block_id)
|
||||
.map(|b| b.access_mode.clone())
|
||||
.unwrap_or_default();
|
||||
Self {
|
||||
id: slot.id,
|
||||
start_at: slot.start_at,
|
||||
end_at: slot.end_at,
|
||||
item: slot.item.into(),
|
||||
source_block_id: slot.source_block_id,
|
||||
block_access_mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,6 +200,8 @@ pub struct CurrentBroadcastResponse {
|
||||
/// Seconds elapsed since the start of the current item — use this as the
|
||||
/// initial seek position for the player.
|
||||
pub offset_secs: u32,
|
||||
/// Access mode of the block currently playing. The stream is gated by this.
|
||||
pub block_access_mode: domain::AccessMode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
|
||||
Reference in New Issue
Block a user