feat: enhance database configuration and media handling with PostgreSQL integration and storage quota management

This commit is contained in:
2025-11-03 03:24:55 +01:00
parent 964bcf2655
commit 22857cf056
14 changed files with 271 additions and 76 deletions

View File

@@ -7,6 +7,8 @@ use libertas_core::{
use sqlx::PgPool;
use uuid::Uuid;
use crate::db_models::PostgresAlbum;
#[derive(Clone)]
pub struct PostgresAlbumRepository {
pool: PgPool,
@@ -42,8 +44,8 @@ impl AlbumRepository for PostgresAlbumRepository {
}
async fn find_by_id(&self, id: Uuid) -> CoreResult<Option<Album>> {
sqlx::query_as!(
Album,
let pg_album = sqlx::query_as!(
PostgresAlbum,
r#"
SELECT id, owner_id, name, description, is_public, created_at, updated_at
FROM albums
@@ -53,12 +55,13 @@ impl AlbumRepository for PostgresAlbumRepository {
)
.fetch_optional(&self.pool)
.await
.map_err(|e| CoreError::Database(e.to_string()))
.map_err(|e| CoreError::Database(e.to_string()))?;
Ok(pg_album.map(|a| a.into()))
}
async fn list_by_user(&self, user_id: Uuid) -> CoreResult<Vec<Album>> {
sqlx::query_as!(
Album,
let pg_albums = sqlx::query_as!(
PostgresAlbum,
r#"
SELECT id, owner_id, name, description, is_public, created_at, updated_at
FROM albums
@@ -68,7 +71,9 @@ impl AlbumRepository for PostgresAlbumRepository {
)
.fetch_all(&self.pool)
.await
.map_err(|e| CoreError::Database(e.to_string()))
.map_err(|e| CoreError::Database(e.to_string()))?;
Ok(pg_albums.into_iter().map(|a| a.into()).collect())
}
async fn add_media_to_album(&self, album_id: Uuid, media_ids: &[Uuid]) -> CoreResult<()> {

View File

@@ -1,11 +1,10 @@
use async_trait::async_trait;
use libertas_core::{
error::{CoreError, CoreResult},
models::AlbumPermission,
repositories::AlbumShareRepository,
error::{CoreError, CoreResult}, models::AlbumPermission, repositories::AlbumShareRepository
};
use sqlx::PgPool;
use uuid::Uuid;
use crate::db_models::PostgresAlbumPermission;
#[derive(Clone)]
pub struct PostgresAlbumShareRepository {
@@ -35,7 +34,7 @@ impl AlbumShareRepository for PostgresAlbumShareRepository {
"#,
album_id,
user_id,
permission.as_str() as "album_permission"
PostgresAlbumPermission::from(permission) as PostgresAlbumPermission,
)
.execute(&self.pool)
.await
@@ -49,9 +48,12 @@ impl AlbumShareRepository for PostgresAlbumShareRepository {
album_id: Uuid,
user_id: Uuid,
) -> CoreResult<Option<AlbumPermission>> {
let row = sqlx::query!(
// --- FIX 2: CAST the enum to TEXT in the SQL ---
"SELECT permission::TEXT as permission FROM album_shares WHERE album_id = $1 AND user_id = $2",
let result = sqlx::query!(
r#"
SELECT permission as "permission: PostgresAlbumPermission"
FROM album_shares
WHERE album_id = $1 AND user_id = $2
"#,
album_id,
user_id
)
@@ -59,8 +61,7 @@ impl AlbumShareRepository for PostgresAlbumShareRepository {
.await
.map_err(|e| CoreError::Database(e.to_string()))?;
// This now works because r.permission is a String
Ok(row.map(|r| AlbumPermission::from(r.permission.as_str())))
Ok(result.map(|row| row.permission.into()))
}
async fn is_media_in_shared_album(&self, media_id: Uuid, user_id: Uuid) -> CoreResult<bool> {

View File

@@ -7,6 +7,8 @@ use libertas_core::{
use sqlx::PgPool;
use uuid::Uuid;
use crate::db_models::PostgresMedia;
#[derive(Clone)]
pub struct PostgresMediaRepository {
pool: PgPool,
@@ -44,8 +46,8 @@ impl MediaRepository for PostgresMediaRepository {
}
async fn find_by_hash(&self, hash: &str) -> CoreResult<Option<Media>> {
sqlx::query_as!(
Media,
let pg_media = sqlx::query_as!(
PostgresMedia,
r#"
SELECT id, owner_id, storage_path, original_filename, mime_type, hash, created_at,
extracted_location, width, height
@@ -56,12 +58,14 @@ impl MediaRepository for PostgresMediaRepository {
)
.fetch_optional(&self.pool)
.await
.map_err(|e| CoreError::Database(e.to_string()))
.map_err(|e| CoreError::Database(e.to_string()))?;
Ok(pg_media.map(|m| m.into()))
}
async fn find_by_id(&self, id: Uuid) -> CoreResult<Option<Media>> {
sqlx::query_as!(
Media,
let pg_media = sqlx::query_as!(
PostgresMedia,
r#"
SELECT id, owner_id, storage_path, original_filename, mime_type, hash, created_at,
extracted_location, width, height
@@ -72,12 +76,14 @@ impl MediaRepository for PostgresMediaRepository {
)
.fetch_optional(&self.pool)
.await
.map_err(|e| CoreError::Database(e.to_string()))
.map_err(|e| CoreError::Database(e.to_string()))?;
Ok(pg_media.map(|m| m.into()))
}
async fn list_by_user(&self, user_id: Uuid) -> CoreResult<Vec<Media>> {
sqlx::query_as!(
Media,
let pg_media = sqlx::query_as!(
PostgresMedia,
r#"
SELECT id, owner_id, storage_path, original_filename, mime_type, hash, created_at,
extracted_location, width, height
@@ -88,7 +94,9 @@ impl MediaRepository for PostgresMediaRepository {
)
.fetch_all(&self.pool)
.await
.map_err(|e| CoreError::Database(e.to_string()))
.map_err(|e| CoreError::Database(e.to_string()))?;
Ok(pg_media.into_iter().map(|m| m.into()).collect())
}
async fn update_metadata(

View File

@@ -1,11 +1,13 @@
use async_trait::async_trait;
use libertas_core::{
error::{CoreError, CoreResult},
models::{Role, User},
models::User,
repositories::UserRepository,
};
use sqlx::{PgPool, SqlitePool, types::Uuid};
use crate::db_models::PostgresUser;
#[derive(Clone)]
pub struct PostgresUserRepository {
pool: PgPool,
@@ -54,69 +56,61 @@ impl UserRepository for PostgresUserRepository {
}
async fn find_by_email(&self, email: &str) -> CoreResult<Option<User>> {
let row = sqlx::query!(
r#"SELECT id, username, email, hashed_password, created_at, updated_at, role, storage_quota, storage_used FROM users WHERE email = $1"#,
let pg_user = sqlx::query_as!(
PostgresUser,
r#"
SELECT
id, username, email, hashed_password, created_at, updated_at,
role,
storage_quota, storage_used
FROM users
WHERE email = $1
"#,
email
)
.fetch_optional(&self.pool)
.await
.map_err(|e| CoreError::Database(e.to_string()))?;
Ok(row.map(|r| User {
id: r.id,
username: r.username,
email: r.email,
hashed_password: r.hashed_password,
created_at: r.created_at,
updated_at: r.updated_at,
role: Role::from(r.role.as_str()),
storage_quota: r.storage_quota,
storage_used: r.storage_used,
}))
Ok(pg_user.map(|u| u.into()))
}
async fn find_by_username(&self, username: &str) -> CoreResult<Option<User>> {
let row = sqlx::query!(
r#"SELECT id, username, email, hashed_password, created_at, updated_at, role, storage_quota, storage_used FROM users WHERE username = $1"#,
let pg_user = sqlx::query_as!(
PostgresUser,
r#"
SELECT
id, username, email, hashed_password, created_at, updated_at,
role, storage_quota, storage_used
FROM users
WHERE username = $1
"#,
username
)
.fetch_optional(&self.pool)
.await
.map_err(|e| CoreError::Database(e.to_string()))?;
Ok(row.map(|r| User {
id: r.id,
username: r.username,
email: r.email,
hashed_password: r.hashed_password,
created_at: r.created_at,
updated_at: r.updated_at,
role: Role::from(r.role.as_str()),
storage_quota: r.storage_quota,
storage_used: r.storage_used,
}))
Ok(pg_user.map(|u| u.into()))
}
async fn find_by_id(&self, id: Uuid) -> CoreResult<Option<User>> {
let row = sqlx::query!(
r#"SELECT id, username, email, hashed_password, created_at, updated_at, role, storage_quota, storage_used FROM users WHERE id = $1"#,
let pg_user = sqlx::query_as!(
PostgresUser,
r#"
SELECT
id, username, email, hashed_password, created_at, updated_at,
role,
storage_quota, storage_used
FROM users
WHERE id = $1
"#,
id
)
.fetch_optional(&self.pool)
.await
.map_err(|e| CoreError::Database(e.to_string()))?;
Ok(row.map(|r| User {
id: r.id,
username: r.username,
email: r.email,
hashed_password: r.hashed_password,
created_at: r.created_at,
updated_at: r.updated_at,
role: Role::from(r.role.as_str()),
storage_quota: r.storage_quota,
storage_used: r.storage_used,
}))
Ok(pg_user.map(|u| u.into()))
}
async fn update_storage_used(&self, user_id: Uuid, bytes: i64) -> CoreResult<()> {