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:
2026-03-14 01:45:10 +01:00
parent 924e162563
commit 81df6eb8ff
25 changed files with 635 additions and 53 deletions

View File

@@ -9,7 +9,8 @@ use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::value_objects::{
BlockId, ChannelId, ContentType, FillStrategy, MediaFilter, MediaItemId, RecyclePolicy, SlotId,
AccessMode, BlockId, ChannelId, ContentType, FillStrategy, MediaFilter, MediaItemId,
RecyclePolicy, SlotId,
};
/// A user in the system.
@@ -82,6 +83,8 @@ pub struct Channel {
pub schedule_config: ScheduleConfig,
pub recycle_policy: RecyclePolicy,
pub auto_schedule: bool,
pub access_mode: AccessMode,
pub access_password_hash: Option<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
@@ -102,6 +105,8 @@ impl Channel {
schedule_config: ScheduleConfig::default(),
recycle_policy: RecyclePolicy::default(),
auto_schedule: false,
access_mode: AccessMode::default(),
access_password_hash: None,
created_at: now,
updated_at: now,
}
@@ -176,6 +181,14 @@ pub struct ProgrammingBlock {
/// regardless of what other blocks aired.
#[serde(default)]
pub ignore_recycle_policy: bool,
/// Who can watch the stream during this block. Gates only /stream, not /now.
#[serde(default)]
pub access_mode: AccessMode,
/// Bcrypt/argon2 hash of the block password (when access_mode = PasswordProtected).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub access_password_hash: Option<String>,
}
fn default_true() -> bool {
@@ -198,6 +211,8 @@ impl ProgrammingBlock {
content: BlockContent::Algorithmic { filter, strategy },
loop_on_finish: true,
ignore_recycle_policy: false,
access_mode: AccessMode::default(),
access_password_hash: None,
}
}
@@ -215,6 +230,8 @@ impl ProgrammingBlock {
content: BlockContent::Manual { items },
loop_on_finish: true,
ignore_recycle_policy: false,
access_mode: AccessMode::default(),
access_password_hash: None,
}
}
}

View File

@@ -1,6 +1,17 @@
use serde::{Deserialize, Serialize};
use std::fmt;
/// Controls who can view a channel's broadcast and stream.
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum AccessMode {
#[default]
Public,
PasswordProtected,
AccountRequired,
OwnerOnly,
}
/// Opaque media item identifier — format is provider-specific internally.
/// The domain never inspects the string; it just passes it back to the provider.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]