feat: Implement album and person sharing with user search and a dedicated share dialog.

This commit is contained in:
2025-12-04 00:58:10 +01:00
parent 74d74a128b
commit 7f07169064
23 changed files with 816 additions and 66 deletions

View File

@@ -65,7 +65,7 @@ pub struct PostgresMediaMetadata {
}
#[derive(Debug, Clone, Copy, sqlx::Type, PartialEq, Eq, Deserialize)]
#[sqlx(rename_all = "lowercase")]
#[sqlx(rename_all = "snake_case")]
#[sqlx(type_name = "album_permission")]
pub enum PostgresAlbumPermission {
View,
@@ -103,7 +103,7 @@ pub struct PostgresFaceRegion {
}
#[derive(Debug, Clone, Copy, sqlx::Type, PartialEq, Eq, Deserialize)]
#[sqlx(rename_all = "lowercase")]
#[sqlx(rename_all = "snake_case")]
#[sqlx(type_name = "person_permission")]
pub enum PostgresPersonPermission {
View,

View File

@@ -111,4 +111,61 @@ impl AlbumShareRepository for PostgresAlbumShareRepository {
Ok(result.exists.unwrap_or(false))
}
async fn list_shares_for_album(
&self,
album_id: Uuid,
) -> CoreResult<Vec<(libertas_core::models::User, AlbumPermission)>> {
let rows = sqlx::query!(
r#"
SELECT
u.id, u.username, u.email, u.hashed_password, u.created_at, u.updated_at,
u.role, u.storage_quota, u.storage_used,
ash.permission as "permission: PostgresAlbumPermission"
FROM album_shares ash
JOIN users u ON ash.user_id = u.id
WHERE ash.album_id = $1
"#,
album_id
)
.fetch_all(&self.pool)
.await
.map_err(|e| CoreError::Database(e.to_string()))?;
let result = rows
.into_iter()
.map(|row| {
let user = crate::db_models::PostgresUser {
id: row.id,
username: row.username,
email: row.email,
hashed_password: row.hashed_password,
created_at: row.created_at,
updated_at: row.updated_at,
role: row.role,
storage_quota: row.storage_quota,
storage_used: row.storage_used,
}
.into();
(user, row.permission.into())
})
.collect();
Ok(result)
}
async fn remove_share(&self, album_id: Uuid, user_id: Uuid) -> CoreResult<()> {
sqlx::query!(
r#"
DELETE FROM album_shares
WHERE album_id = $1 AND user_id = $2
"#,
album_id,
user_id
)
.execute(&self.pool)
.await
.map_err(|e| CoreError::Database(e.to_string()))?;
Ok(())
}
}

View File

@@ -4,7 +4,7 @@ use libertas_core::{
models::{Person, PersonPermission},
repositories::PersonShareRepository,
};
use sqlx::{types::Uuid, PgPool};
use sqlx::{PgPool, types::Uuid};
use crate::db_models::{PostgresPersonPermission, PostgresPersonShared};
@@ -103,4 +103,46 @@ impl PersonShareRepository for PostgresPersonShareRepository {
.map(PostgresPersonShared::into)
.collect())
}
}
async fn list_shares_for_person(
&self,
person_id: Uuid,
) -> CoreResult<Vec<(libertas_core::models::User, PersonPermission)>> {
let rows = sqlx::query!(
r#"
SELECT
u.id, u.username, u.email, u.hashed_password, u.created_at, u.updated_at,
u.role, u.storage_quota, u.storage_used,
ps.permission as "permission: PostgresPersonPermission"
FROM person_shares ps
JOIN users u ON ps.user_id = u.id
WHERE ps.person_id = $1
"#,
person_id
)
.fetch_all(&self.pool)
.await
.map_err(|e| CoreError::Database(e.to_string()))?;
let result = rows
.into_iter()
.map(|row| {
let user = crate::db_models::PostgresUser {
id: row.id,
username: row.username,
email: row.email,
hashed_password: row.hashed_password,
created_at: row.created_at,
updated_at: row.updated_at,
role: row.role,
storage_quota: row.storage_quota,
storage_used: row.storage_used,
}
.into();
(user, row.permission.into())
})
.collect();
Ok(result)
}
}

View File

@@ -137,6 +137,27 @@ impl UserRepository for PostgresUserRepository {
async fn update_storage_used(&self, user_id: Uuid, bytes: i64) -> CoreResult<()> {
Self::update_storage_used_internal(&self.pool, user_id, bytes).await
}
async fn search_users(&self, query: &str) -> CoreResult<Vec<User>> {
let pattern = format!("%{}%", query);
let pg_users = sqlx::query_as!(
PostgresUser,
r#"
SELECT
id, username, email, hashed_password, created_at, updated_at,
role, storage_quota, storage_used
FROM users
WHERE username ILIKE $1 OR email ILIKE $1
LIMIT 10
"#,
pattern
)
.fetch_all(&self.pool)
.await
.map_err(|e| CoreError::Database(e.to_string()))?;
Ok(pg_users.into_iter().map(|u| u.into()).collect())
}
}
#[async_trait]
@@ -165,4 +186,9 @@ impl UserRepository for SqliteUserRepository {
println!("SQLITE REPO: Updating user storage used");
Ok(())
}
async fn search_users(&self, _query: &str) -> CoreResult<Vec<User>> {
println!("SQLITE REPO: Searching users");
Ok(Vec::new())
}
}