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:
@@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
|
||||
use sqlx::FromRow;
|
||||
use uuid::Uuid;
|
||||
|
||||
use domain::{Channel, ChannelId, DomainError, RecyclePolicy, ScheduleConfig, UserId};
|
||||
use domain::{AccessMode, Channel, ChannelId, DomainError, RecyclePolicy, ScheduleConfig, UserId};
|
||||
|
||||
#[derive(Debug, FromRow)]
|
||||
pub(super) struct ChannelRow {
|
||||
@@ -14,6 +14,8 @@ pub(super) struct ChannelRow {
|
||||
pub schedule_config: String,
|
||||
pub recycle_policy: String,
|
||||
pub auto_schedule: i64,
|
||||
pub access_mode: String,
|
||||
pub access_password_hash: Option<String>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
}
|
||||
@@ -44,6 +46,11 @@ impl TryFrom<ChannelRow> for Channel {
|
||||
DomainError::RepositoryError(format!("Invalid recycle_policy JSON: {}", e))
|
||||
})?;
|
||||
|
||||
let access_mode: AccessMode = serde_json::from_value(
|
||||
serde_json::Value::String(row.access_mode),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(Channel {
|
||||
id,
|
||||
owner_id,
|
||||
@@ -53,6 +60,8 @@ impl TryFrom<ChannelRow> for Channel {
|
||||
schedule_config,
|
||||
recycle_policy,
|
||||
auto_schedule: row.auto_schedule != 0,
|
||||
access_mode,
|
||||
access_password_hash: row.access_password_hash,
|
||||
created_at: parse_dt(&row.created_at)?,
|
||||
updated_at: parse_dt(&row.updated_at)?,
|
||||
})
|
||||
@@ -60,4 +69,4 @@ impl TryFrom<ChannelRow> for Channel {
|
||||
}
|
||||
|
||||
pub(super) const SELECT_COLS: &str =
|
||||
"id, owner_id, name, description, timezone, schedule_config, recycle_policy, auto_schedule, created_at, updated_at";
|
||||
"id, owner_id, name, description, timezone, schedule_config, recycle_policy, auto_schedule, access_mode, access_password_hash, created_at, updated_at";
|
||||
|
||||
@@ -58,19 +58,26 @@ impl ChannelRepository for PostgresChannelRepository {
|
||||
DomainError::RepositoryError(format!("Failed to serialize recycle_policy: {}", e))
|
||||
})?;
|
||||
|
||||
let access_mode = serde_json::to_value(&channel.access_mode)
|
||||
.ok()
|
||||
.and_then(|v| v.as_str().map(str::to_owned))
|
||||
.unwrap_or_else(|| "public".to_owned());
|
||||
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO channels
|
||||
(id, owner_id, name, description, timezone, schedule_config, recycle_policy, auto_schedule, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
||||
(id, owner_id, name, description, timezone, schedule_config, recycle_policy, auto_schedule, access_mode, access_password_hash, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
name = EXCLUDED.name,
|
||||
description = EXCLUDED.description,
|
||||
timezone = EXCLUDED.timezone,
|
||||
schedule_config = EXCLUDED.schedule_config,
|
||||
recycle_policy = EXCLUDED.recycle_policy,
|
||||
auto_schedule = EXCLUDED.auto_schedule,
|
||||
updated_at = EXCLUDED.updated_at
|
||||
name = EXCLUDED.name,
|
||||
description = EXCLUDED.description,
|
||||
timezone = EXCLUDED.timezone,
|
||||
schedule_config = EXCLUDED.schedule_config,
|
||||
recycle_policy = EXCLUDED.recycle_policy,
|
||||
auto_schedule = EXCLUDED.auto_schedule,
|
||||
access_mode = EXCLUDED.access_mode,
|
||||
access_password_hash = EXCLUDED.access_password_hash,
|
||||
updated_at = EXCLUDED.updated_at
|
||||
"#,
|
||||
)
|
||||
.bind(channel.id.to_string())
|
||||
@@ -81,6 +88,8 @@ impl ChannelRepository for PostgresChannelRepository {
|
||||
.bind(&schedule_config)
|
||||
.bind(&recycle_policy)
|
||||
.bind(channel.auto_schedule as i64)
|
||||
.bind(&access_mode)
|
||||
.bind(&channel.access_password_hash)
|
||||
.bind(channel.created_at.to_rfc3339())
|
||||
.bind(channel.updated_at.to_rfc3339())
|
||||
.execute(&self.pool)
|
||||
|
||||
@@ -58,19 +58,26 @@ impl ChannelRepository for SqliteChannelRepository {
|
||||
DomainError::RepositoryError(format!("Failed to serialize recycle_policy: {}", e))
|
||||
})?;
|
||||
|
||||
let access_mode = serde_json::to_value(&channel.access_mode)
|
||||
.ok()
|
||||
.and_then(|v| v.as_str().map(str::to_owned))
|
||||
.unwrap_or_else(|| "public".to_owned());
|
||||
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO channels
|
||||
(id, owner_id, name, description, timezone, schedule_config, recycle_policy, auto_schedule, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
(id, owner_id, name, description, timezone, schedule_config, recycle_policy, auto_schedule, access_mode, access_password_hash, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(id) DO UPDATE SET
|
||||
name = excluded.name,
|
||||
description = excluded.description,
|
||||
timezone = excluded.timezone,
|
||||
schedule_config = excluded.schedule_config,
|
||||
recycle_policy = excluded.recycle_policy,
|
||||
auto_schedule = excluded.auto_schedule,
|
||||
updated_at = excluded.updated_at
|
||||
name = excluded.name,
|
||||
description = excluded.description,
|
||||
timezone = excluded.timezone,
|
||||
schedule_config = excluded.schedule_config,
|
||||
recycle_policy = excluded.recycle_policy,
|
||||
auto_schedule = excluded.auto_schedule,
|
||||
access_mode = excluded.access_mode,
|
||||
access_password_hash = excluded.access_password_hash,
|
||||
updated_at = excluded.updated_at
|
||||
"#,
|
||||
)
|
||||
.bind(channel.id.to_string())
|
||||
@@ -81,6 +88,8 @@ impl ChannelRepository for SqliteChannelRepository {
|
||||
.bind(&schedule_config)
|
||||
.bind(&recycle_policy)
|
||||
.bind(channel.auto_schedule as i64)
|
||||
.bind(&access_mode)
|
||||
.bind(&channel.access_password_hash)
|
||||
.bind(channel.created_at.to_rfc3339())
|
||||
.bind(channel.updated_at.to_rfc3339())
|
||||
.execute(&self.pool)
|
||||
|
||||
Reference in New Issue
Block a user