2025-05-08 22:18:04 -04:00
use super ::* ;
use crate ::cache ::Cache ;
use crate ::model ::{
Error , Result ,
auth ::User ,
permissions ::FinePermission ,
stacks ::{ StackPrivacy , UserStack } ,
} ;
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 [`UserStack`] from an SQL row.
pub ( crate ) fn get_stack_from_row (
#[ cfg(feature = " sqlite " ) ] x : & Row < '_ > ,
#[ cfg(feature = " postgres " ) ] x : & Row ,
) -> UserStack {
UserStack {
id : get ! ( x ->0 ( i64 ) ) as usize ,
created : get ! ( x ->1 ( i64 ) ) as usize ,
owner : get ! ( x ->2 ( i64 ) ) as usize ,
name : get ! ( x ->3 ( String ) ) ,
users : serde_json ::from_str ( & get! ( x ->4 ( String ) ) ) . unwrap ( ) ,
privacy : serde_json ::from_str ( & get! ( x ->5 ( String ) ) ) . unwrap ( ) ,
}
}
auto_method! ( get_stack_by_id ( usize as i64 ) @ get_stack_from_row -> " SELECT * FROM stacks WHERE id = $1 " - - name = " stack " - - returns = UserStack - - cache - key - tmpl = " atto.stack:{} " ) ;
/// Get all stacks by user.
///
/// # Arguments
/// * `id` - the ID of the user to fetch stacks for
pub async fn get_stacks_by_owner ( & self , id : usize ) -> Result < Vec < UserStack > > {
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 stacks WHERE owner = $1 ORDER BY name ASC " ,
& [ & ( id as i64 ) ] ,
| x | { Self ::get_stack_from_row ( x ) }
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " stack " . to_string ( ) ) ) ;
}
Ok ( res . unwrap ( ) )
}
/// Create a new stack in the database.
///
/// # Arguments
/// * `data` - a mock [`UserStack`] object to insert
pub async fn create_stack ( & self , data : UserStack ) -> Result < UserStack > {
2025-05-08 22:35:05 -04:00
// check values
if data . name . len ( ) < 2 {
return Err ( Error ::DataTooShort ( " title " . to_string ( ) ) ) ;
} else if data . name . len ( ) > 32 {
return Err ( Error ::DataTooLong ( " title " . to_string ( ) ) ) ;
}
2025-05-08 22:18:04 -04:00
// check number of stacks
let owner = self . get_user_by_id ( data . owner ) . await ? ;
if ! owner . permissions . check ( FinePermission ::SUPPORTER ) {
let stacks = self . get_stacks_by_owner ( data . owner ) . await ? ;
let maximum_count = 5 ;
if stacks . len ( ) > = maximum_count {
return Err ( Error ::MiscError (
" You already have the maximum number of stacks you can have " . to_string ( ) ,
) ) ;
}
}
// ...
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! (
& conn ,
" INSERT INTO stacks VALUES ($1, $2, $3, $4, $5, $6) " ,
params! [
& ( data . id as i64 ) ,
& ( data . created as i64 ) ,
& ( data . owner as i64 ) ,
& data . name ,
& serde_json ::to_string ( & data . users ) . unwrap ( ) ,
& serde_json ::to_string ( & data . privacy ) . unwrap ( ) ,
]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
Ok ( data )
}
pub async fn delete_stack ( & self , id : usize , user : & User ) -> Result < ( ) > {
let stack = self . get_stack_by_id ( id ) . await ? ;
// check user permission
if user . id ! = stack . owner {
if ! user . permissions . check ( FinePermission ::MANAGE_STACKS ) {
return Err ( Error ::NotAllowed ) ;
}
}
// ...
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! ( & conn , " DELETE FROM stacks WHERE id = $1 " , & [ & ( id as i64 ) ] ) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
self . 2. remove ( format! ( " atto.stack: {} " , id ) ) . await ;
Ok ( ( ) )
}
auto_method! ( update_stack_name ( & str ) @ get_stack_by_id :MANAGE_STACKS -> " UPDATE stacks SET name = $1 WHERE id = $2 " - - cache - key - tmpl = " atto.stack:{} " ) ;
auto_method! ( update_stack_privacy ( StackPrivacy ) @ get_stack_by_id :MANAGE_STACKS -> " UPDATE stacks SET privacy = $1 WHERE id = $2 " - - serde - - cache - key - tmpl = " atto.stack:{} " ) ;
auto_method! ( update_stack_users ( Vec < usize > ) @ get_stack_by_id :MANAGE_STACKS -> " UPDATE stacks SET users = $1 WHERE id = $2 " - - serde - - cache - key - tmpl = " atto.stack:{} " ) ;
}