2025-06-08 14:15:42 -04:00
use oiseau ::cache ::Cache ;
2025-05-11 15:20:15 -04:00
use crate ::model ::auth ::User ;
use crate ::model ::permissions ::FinePermission ;
2025-05-05 19:38:01 -04:00
use crate ::model ::{ Error , Result , uploads ::MediaUpload } ;
2025-06-08 14:15:42 -04:00
use crate ::{ auto_method , DataManager } ;
2025-05-05 19:38:01 -04:00
#[ cfg(feature = " sqlite " ) ]
2025-06-08 14:15:42 -04:00
use oiseau ::SqliteRow ;
2025-05-05 19:38:01 -04:00
#[ cfg(feature = " postgres " ) ]
2025-06-08 14:15:42 -04:00
use oiseau ::PostgresRow ;
use oiseau ::{ execute , get , query_rows , params } ;
2025-05-05 19:38:01 -04:00
impl DataManager {
/// Get a [`MediaUpload`] from an SQL row.
pub ( crate ) fn get_upload_from_row (
2025-06-08 14:15:42 -04:00
#[ cfg(feature = " sqlite " ) ] x : & SqliteRow < '_ > ,
#[ cfg(feature = " postgres " ) ] x : & PostgresRow ,
2025-05-05 19:38:01 -04:00
) -> MediaUpload {
MediaUpload {
id : get ! ( x ->0 ( i64 ) ) as usize ,
created : get ! ( x ->1 ( i64 ) ) as usize ,
owner : get ! ( x ->2 ( i64 ) ) as usize ,
what : serde_json ::from_str ( & get! ( x ->3 ( String ) ) ) . unwrap ( ) ,
}
}
auto_method! ( get_upload_by_id ( usize as i64 ) @ get_upload_from_row -> " SELECT * FROM uploads WHERE id = $1 " - - name = " upload " - - returns = MediaUpload - - cache - key - tmpl = " atto.uploads:{} " ) ;
/// Get all uploads (paginated).
///
/// # Arguments
/// * `batch` - the limit of items in each page
/// * `page` - the page number
pub async fn get_uploads ( & self , batch : usize , page : usize ) -> Result < Vec < MediaUpload > > {
2025-06-08 14:15:42 -04:00
let conn = match self . 0. connect ( ) . await {
2025-05-05 19:38:01 -04:00
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = query_rows! (
& conn ,
" SELECT * FROM uploads ORDER BY created DESC LIMIT $1 OFFSET $2 " ,
& [ & ( batch as i64 ) , & ( ( page * batch ) as i64 ) ] ,
| x | { Self ::get_upload_from_row ( x ) }
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " upload " . to_string ( ) ) ) ;
}
Ok ( res . unwrap ( ) )
}
2025-05-11 15:20:15 -04:00
/// Get all uploads by their owner (paginated).
///
/// # Arguments
/// * `owner` - the ID of the owner of the upload
/// * `batch` - the limit of items in each page
/// * `page` - the page number
pub async fn get_uploads_by_owner (
& self ,
owner : usize ,
batch : usize ,
page : usize ,
) -> Result < Vec < MediaUpload > > {
2025-06-08 14:15:42 -04:00
let conn = match self . 0. connect ( ) . await {
2025-05-11 15:20:15 -04:00
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = query_rows! (
& conn ,
" SELECT * FROM uploads WHERE owner = $1 ORDER BY created DESC LIMIT $2 OFFSET $3 " ,
& [ & ( owner as i64 ) , & ( batch as i64 ) , & ( ( page * batch ) as i64 ) ] ,
| x | { Self ::get_upload_from_row ( x ) }
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " upload " . to_string ( ) ) ) ;
}
Ok ( res . unwrap ( ) )
}
2025-06-07 21:18:28 -04:00
/// Get all uploads by their owner.
///
/// # Arguments
/// * `owner` - the ID of the owner of the upload
pub async fn get_uploads_by_owner_all ( & self , owner : usize ) -> Result < Vec < MediaUpload > > {
2025-06-08 14:15:42 -04:00
let conn = match self . 0. connect ( ) . await {
2025-06-07 21:18:28 -04:00
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = query_rows! (
& conn ,
" SELECT * FROM uploads WHERE owner = $1 ORDER BY created DESC " ,
& [ & ( owner as i64 ) ] ,
| x | { Self ::get_upload_from_row ( x ) }
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " upload " . to_string ( ) ) ) ;
}
Ok ( res . unwrap ( ) )
}
2025-05-05 19:38:01 -04:00
/// Create a new upload in the database.
///
/// # Arguments
/// * `data` - a mock [`MediaUpload`] object to insert
2025-05-10 21:58:02 -04:00
pub async fn create_upload ( & self , data : MediaUpload ) -> Result < MediaUpload > {
2025-06-08 14:15:42 -04:00
let conn = match self . 0. connect ( ) . await {
2025-05-05 19:38:01 -04:00
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! (
& conn ,
" INSERT INTO uploads VALUES ($1, $2, $3, $4) " ,
params! [
& ( data . id as i64 ) ,
& ( data . created as i64 ) ,
& ( data . owner as i64 ) ,
& serde_json ::to_string ( & data . what ) . unwrap ( ) . as_str ( ) ,
]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
// return
2025-05-10 21:58:02 -04:00
Ok ( data )
2025-05-05 19:38:01 -04:00
}
pub async fn delete_upload ( & self , id : usize ) -> Result < ( ) > {
// if !user.permissions.check(FinePermission::MANAGE_UPLOADS) {
// return Err(Error::NotAllowed);
// }
// delete file
// it's most important that the file gets off the file system first, even
// if there's an issue in the database
//
// the actual file takes up much more space than the database entry.
2025-05-10 21:58:02 -04:00
let upload = self . get_upload_by_id ( id ) . await ? ;
2025-06-08 14:15:42 -04:00
upload . remove ( & self . 0.0 ) ? ;
2025-05-05 19:38:01 -04:00
// delete from database
2025-06-08 14:15:42 -04:00
let conn = match self . 0. connect ( ) . await {
2025-05-05 19:38:01 -04:00
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! ( & conn , " DELETE FROM uploads WHERE id = $1 " , & [ & ( id as i64 ) ] ) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
2025-06-08 14:15:42 -04:00
self . 0. 1. remove ( format! ( " atto.upload: {} " , id ) ) . await ;
2025-05-05 19:38:01 -04:00
// return
Ok ( ( ) )
}
2025-05-11 15:20:15 -04:00
pub async fn delete_upload_checked ( & self , id : usize , user : & User ) -> Result < ( ) > {
let upload = self . get_upload_by_id ( id ) . await ? ;
// check user permission
if user . id ! = upload . owner & & ! user . permissions . check ( FinePermission ::MANAGE_UPLOADS ) {
return Err ( Error ::NotAllowed ) ;
}
// delete file
2025-06-08 14:15:42 -04:00
upload . remove ( & self . 0.0 ) ? ;
2025-05-11 15:20:15 -04:00
// ...
2025-06-08 14:15:42 -04:00
let conn = match self . 0. connect ( ) . await {
2025-05-11 15:20:15 -04:00
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! ( & conn , " DELETE FROM uploads WHERE id = $1 " , & [ & ( id as i64 ) ] ) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
2025-06-08 14:15:42 -04:00
self . 0. 1. remove ( format! ( " atto.upload: {} " , id ) ) . await ;
2025-05-11 15:20:15 -04:00
Ok ( ( ) )
}
2025-05-05 19:38:01 -04:00
}