Files
k-photos/crates/domain/src/identity/ports.rs
Gabriel Kaszewski 957737ac9b feat: frontend MVP — auth, timeline, upload, albums, admin, image viewer
Backend:
- user roles (DB + JWT + first-user-is-admin)
- volume-aware file resolver (multi-volume asset serving)
- directory scanner uses volume URI directly
- date-summary endpoint (capture date from EXIF)
- timeline ordered by capture date
- list endpoints: volumes, plugins, pipelines, library paths
- delete endpoints: volumes, library paths
- configurable upload body limit (MAX_UPLOAD_BYTES)

Frontend:
- auth: login/register, token refresh, role-based admin gate
- timeline: date-grouped grid, infinite scroll, date scrubber
- image viewer: fullscreen zoom/pan/pinch, metadata sidebar
- upload: drag-drop, sequential upload, progress tracking
- albums: create, add/remove photos, asset picker dialog
- admin: storage (import library), jobs (pagination, error details),
  plugins (list + toggle), pipelines, sidecars, duplicates
- multi-select mode with add-to-album action
- TanStack Query for all data fetching
2026-06-01 01:35:43 +02:00

62 lines
2.4 KiB
Rust

use super::entities::{Group, RefreshToken, Role, User};
use crate::common::errors::DomainError;
use crate::common::value_objects::{Email, PasswordHash, SystemId};
use async_trait::async_trait;
// --- UserRepository ---
#[async_trait]
pub trait UserRepository: Send + Sync {
async fn find_by_id(&self, id: &SystemId) -> Result<Option<User>, DomainError>;
async fn find_by_email(&self, email: &Email) -> Result<Option<User>, DomainError>;
async fn find_by_username(&self, username: &str) -> Result<Option<User>, DomainError>;
async fn save(&self, user: &User) -> Result<(), DomainError>;
async fn delete(&self, id: &SystemId) -> Result<(), DomainError>;
async fn count(&self) -> Result<u64, DomainError>;
}
// --- RoleRepository ---
#[async_trait]
pub trait RoleRepository: Send + Sync {
async fn find_by_id(&self, id: &SystemId) -> Result<Option<Role>, DomainError>;
async fn find_by_name(&self, name: &str) -> Result<Option<Role>, DomainError>;
async fn find_defaults(&self) -> Result<Vec<Role>, DomainError>;
async fn save(&self, role: &Role) -> Result<(), DomainError>;
async fn delete(&self, id: &SystemId) -> Result<(), DomainError>;
}
// --- GroupRepository ---
#[async_trait]
pub trait GroupRepository: Send + Sync {
async fn find_by_id(&self, id: &SystemId) -> Result<Option<Group>, DomainError>;
async fn find_by_user(&self, user_id: &SystemId) -> Result<Vec<Group>, DomainError>;
async fn save(&self, group: &Group) -> Result<(), DomainError>;
async fn delete(&self, id: &SystemId) -> Result<(), DomainError>;
}
// --- RefreshTokenRepository ---
#[async_trait]
pub trait RefreshTokenRepository: Send + Sync {
async fn save(&self, token: &RefreshToken) -> Result<(), DomainError>;
async fn find_by_hash(&self, token_hash: &str) -> Result<Option<RefreshToken>, DomainError>;
async fn delete_by_user(&self, user_id: &SystemId) -> Result<(), DomainError>;
async fn delete(&self, id: &SystemId) -> Result<(), DomainError>;
}
// --- Auth ---
#[async_trait]
pub trait PasswordHasher: Send + Sync {
async fn hash(&self, password: &str) -> Result<PasswordHash, DomainError>;
async fn verify(&self, password: &str, hash: &PasswordHash) -> Result<bool, DomainError>;
}
#[async_trait]
pub trait TokenIssuer: Send + Sync {
async fn issue(&self, user_id: &SystemId, role: &str) -> Result<String, DomainError>;
async fn verify(&self, token: &str) -> Result<(SystemId, String), DomainError>;
}