2025-04-25 16:22:38 -04:00
use super ::* ;
use crate ::cache ::Cache ;
2025-04-27 23:11:37 -04:00
use crate ::model ::moderation ::AuditLogEntry ;
2025-04-25 16:22:38 -04:00
use crate ::model ::{
Error , Result , auth ::User , permissions ::FinePermission ,
communities_permissions ::CommunityPermission , channels ::Channel ,
} ;
use crate ::{ auto_method , execute , get , query_row , query_rows , params } ;
#[ cfg(feature = " sqlite " ) ]
use rusqlite ::Row ;
#[ cfg(feature = " postgres " ) ]
use tokio_postgres ::Row ;
impl DataManager {
/// Get a [`Channel`] from an SQL row.
pub ( crate ) fn get_channel_from_row (
#[ cfg(feature = " sqlite " ) ] x : & Row < '_ > ,
#[ cfg(feature = " postgres " ) ] x : & Row ,
) -> Channel {
Channel {
id : get ! ( x ->0 ( i64 ) ) as usize ,
community : get ! ( x ->1 ( i64 ) ) as usize ,
owner : get ! ( x ->2 ( i64 ) ) as usize ,
created : get ! ( x ->3 ( i64 ) ) as usize ,
minimum_role_read : get ! ( x ->4 ( i32 ) ) as u32 ,
minimum_role_write : get ! ( x ->5 ( i32 ) ) as u32 ,
position : get ! ( x ->6 ( i32 ) ) as usize ,
2025-04-27 23:11:37 -04:00
members : serde_json ::from_str ( & get! ( x ->7 ( String ) ) ) . unwrap ( ) ,
title : get ! ( x ->8 ( String ) ) ,
2025-04-25 16:22:38 -04:00
}
}
2025-04-27 23:11:37 -04:00
auto_method! ( get_channel_by_id ( usize as i64 ) @ get_channel_from_row -> " SELECT * FROM channels WHERE id = $1 " - - name = " channel " - - returns = Channel - - cache - key - tmpl = " atto.channel:{} " ) ;
2025-04-25 16:22:38 -04:00
2025-04-27 23:11:37 -04:00
/// Get all channels by community.
2025-04-25 16:22:38 -04:00
///
/// # Arguments
/// * `community` - the ID of the community to fetch channels for
pub async fn get_channels_by_community ( & self , community : usize ) -> Result < Vec < Channel > > {
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = query_rows! (
& conn ,
2025-04-27 23:11:37 -04:00
" SELECT * FROM channels WHERE community = $1 ORDER BY position ASC " ,
2025-04-25 16:22:38 -04:00
& [ & ( community as i64 ) ] ,
| x | { Self ::get_channel_from_row ( x ) }
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " channel " . to_string ( ) ) ) ;
}
Ok ( res . unwrap ( ) )
}
2025-04-27 23:11:37 -04:00
/// Get all channels by user.
///
/// # Arguments
/// * `user` - the ID of the user to fetch channels for
pub async fn get_channels_by_user ( & self , user : usize ) -> Result < Vec < Channel > > {
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = query_rows! (
& conn ,
" SELECT * FROM channels WHERE (owner = $1 OR members LIKE $2) AND community = 0 ORDER BY created DESC " ,
params! [ & ( user as i64 ) , & format! ( " % {user} % " ) ] ,
| x | { Self ::get_channel_from_row ( x ) }
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " channel " . to_string ( ) ) ) ;
}
Ok ( res . unwrap ( ) )
}
/// Get a channel given its `owner` and a member.
///
/// # Arguments
/// * `owner` - the ID of the owner
/// * `member` - the ID of the member
pub async fn get_channel_by_owner_member (
& self ,
owner : usize ,
member : usize ,
) -> Result < Channel > {
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = query_row! (
& conn ,
" SELECT * FROM channels WHERE owner = $1 AND members = $2 AND community = 0 ORDER BY created DESC " ,
params! [ & ( owner as i64 ) , & format! ( " [ {member} ] " ) ] ,
| x | { Ok ( Self ::get_channel_from_row ( x ) ) }
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " channel " . to_string ( ) ) ) ;
}
Ok ( res . unwrap ( ) )
}
2025-04-25 16:22:38 -04:00
/// Create a new channel in the database.
///
/// # Arguments
/// * `data` - a mock [`Channel`] object to insert
pub async fn create_channel ( & self , data : Channel ) -> Result < ( ) > {
let user = self . get_user_by_id ( data . owner ) . await ? ;
// check user permission in community
2025-04-27 23:11:37 -04:00
if data . community ! = 0 {
let membership = self
. get_membership_by_owner_community ( user . id , data . community )
. await ? ;
if ! membership . role . check ( CommunityPermission ::MANAGE_CHANNELS )
& & ! user . permissions . check ( FinePermission ::MANAGE_CHANNELS )
{
return Err ( Error ::NotAllowed ) ;
}
2025-04-25 16:22:38 -04:00
}
// ...
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! (
& conn ,
2025-04-27 23:11:37 -04:00
" INSERT INTO channels VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) " ,
2025-04-25 16:22:38 -04:00
params! [
& ( data . id as i64 ) ,
& ( data . community as i64 ) ,
& ( data . owner as i64 ) ,
& ( data . created as i64 ) ,
& ( data . minimum_role_read as i32 ) ,
& ( data . minimum_role_write as i32 ) ,
2025-04-27 23:11:37 -04:00
& ( data . position as i32 ) ,
& serde_json ::to_string ( & data . members ) . unwrap ( ) ,
& data . title
2025-04-25 16:22:38 -04:00
]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
Ok ( ( ) )
}
2025-04-27 23:11:37 -04:00
pub async fn delete_channel ( & self , id : usize , user : & User ) -> Result < ( ) > {
2025-04-25 16:22:38 -04:00
let channel = self . get_channel_by_id ( id ) . await ? ;
// check user permission in community
2025-04-27 23:11:37 -04:00
if user . id ! = channel . owner {
let membership = self
. get_membership_by_owner_community ( user . id , channel . community )
. await ? ;
if ! membership . role . check ( CommunityPermission ::MANAGE_CHANNELS ) {
return Err ( Error ::NotAllowed ) ;
}
2025-04-25 16:22:38 -04:00
}
// ...
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! ( & conn , " DELETE FROM channels WHERE id = $1 " , & [ & ( id as i64 ) ] ) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
2025-04-27 23:11:37 -04:00
// delete messages
let res = execute! (
& conn ,
" DELETE FROM messages WHERE channel = $1 " ,
& [ & ( id as i64 ) ]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
// ...
self . 2. remove ( format! ( " atto.channel: {} " , id ) ) . await ;
Ok ( ( ) )
}
pub async fn remove_channel_member ( & self , id : usize , user : User , member : usize ) -> Result < ( ) > {
let mut y = self . get_channel_by_id ( id ) . await ? ;
if user . id ! = y . owner & & member ! = user . id {
if ! user . permissions . check ( FinePermission ::MANAGE_CHANNELS ) {
return Err ( Error ::NotAllowed ) ;
} else {
self . create_audit_log_entry ( AuditLogEntry ::new (
user . id ,
format! ( " invoked `remove_channel_member` with x value ` {member} ` " ) ,
) )
. await ?
}
}
y . members
. remove ( y . members . iter ( ) . position ( | x | * x = = member ) . unwrap ( ) ) ;
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! (
& conn ,
" UPDATE channels SET members = $1 WHERE id = $2 " ,
params! [ & serde_json ::to_string ( & y . members ) . unwrap ( ) , & ( id as i64 ) ]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
2025-04-25 16:22:38 -04:00
self . 2. remove ( format! ( " atto.channel: {} " , id ) ) . await ;
2025-04-27 23:11:37 -04:00
2025-04-25 16:22:38 -04:00
Ok ( ( ) )
}
2025-04-27 23:11:37 -04:00
auto_method! ( update_channel_title ( & str ) @ get_channel_by_id :MANAGE_CHANNELS -> " UPDATE channels SET title = $1 WHERE id = $2 " - - cache - key - tmpl = " atto.channel:{} " ) ;
auto_method! ( update_channel_position ( i32 ) @ get_channel_by_id :MANAGE_CHANNELS -> " UPDATE channels SET position = $1 WHERE id = $2 " - - cache - key - tmpl = " atto.channel:{} " ) ;
auto_method! ( update_channel_minimum_role_read ( i32 ) @ get_channel_by_id :MANAGE_CHANNELS -> " UPDATE channels SET minimum_role_read = $1 WHERE id = $2 " - - cache - key - tmpl = " atto.channel:{} " ) ;
auto_method! ( update_channel_minimum_role_write ( i32 ) @ get_channel_by_id :MANAGE_CHANNELS -> " UPDATE channels SET minimum_role_write = $1 WHERE id = $2 " - - cache - key - tmpl = " atto.channel:{} " ) ;
auto_method! ( update_channel_members ( Vec < usize > ) @ get_channel_by_id :MANAGE_CHANNELS -> " UPDATE channels SET members = $1 WHERE id = $2 " - - serde - - cache - key - tmpl = " atto.channel:{} " ) ;
2025-04-25 16:22:38 -04:00
}