view image files

This commit is contained in:
2025-07-23 13:47:42 +02:00
parent b3e68deb38
commit 7f8726c225
23 changed files with 553 additions and 45 deletions

View File

@@ -1,40 +1,65 @@
use std::os::unix::fs::MetadataExt;
use anyhow::{Context, anyhow, bail};
use rustix::fs::statx;
use tokio::{
fs,
io::{self, AsyncWriteExt as _},
};
use tokio_util::io::ReaderStream;
use crate::domain::warren::{
models::file::{
AbsoluteFilePath, CreateDirectoryError, CreateDirectoryRequest, CreateFileError,
CreateFileRequest, DeleteDirectoryError, DeleteDirectoryRequest, DeleteFileError,
DeleteFileRequest, File, FileMimeType, FileName, FilePath, FileType, ListFilesError,
ListFilesRequest, RenameEntryError, RenameEntryRequest,
use crate::{
config::Config,
domain::warren::{
models::file::{
AbsoluteFilePath, CreateDirectoryError, CreateDirectoryRequest, CreateFileError,
CreateFileRequest, DeleteDirectoryError, DeleteDirectoryRequest, DeleteFileError,
DeleteFileRequest, FetchFileError, FetchFileRequest, File, FileMimeType, FileName,
FilePath, FileStream, FileType, ListFilesError, ListFilesRequest, RenameEntryError,
RenameEntryRequest,
},
ports::FileSystemRepository,
},
ports::FileSystemRepository,
};
const MAX_FILE_FETCH_BYTES: &str = "MAX_FILE_FETCH_BYTES";
#[derive(Debug, Clone)]
pub struct FileSystemConfig {
base_directory: String,
max_file_fetch_bytes: u64,
}
impl FileSystemConfig {
pub fn new(base_directory: String) -> Self {
Self { base_directory }
pub fn new(base_directory: String, max_file_fetch_bytes: u64) -> Self {
Self {
base_directory,
max_file_fetch_bytes,
}
}
pub fn from_env(serve_dir: String) -> anyhow::Result<Self> {
// 268435456 bytes = 0.25GB
let max_file_fetch_bytes: u64 = match Config::load_env(MAX_FILE_FETCH_BYTES) {
Ok(value) => value.parse()?,
Err(_) => 268435456,
};
Ok(Self::new(serve_dir, max_file_fetch_bytes))
}
}
#[derive(Debug, Clone)]
pub struct FileSystem {
base_directory: FilePath,
max_file_fetch_bytes: u64,
}
impl FileSystem {
pub fn new(config: FileSystemConfig) -> anyhow::Result<Self> {
let file_system = Self {
base_directory: FilePath::new(&config.base_directory)?,
max_file_fetch_bytes: config.max_file_fetch_bytes,
};
Ok(file_system)
@@ -146,6 +171,27 @@ impl FileSystem {
Ok(path)
}
async fn fetch_file(&self, path: &AbsoluteFilePath) -> anyhow::Result<FileStream> {
let path = self.get_target_path(path);
let file = fs::OpenOptions::new()
.create(false)
.write(false)
.read(true)
.open(&path)
.await?;
let file_size = file.metadata().await?.size();
if file_size > self.max_file_fetch_bytes {
bail!("File size exceeds configured limit");
}
let stream = FileStream::new(ReaderStream::new(file));
Ok(stream)
}
/// Actually removes a file from the underlying file system
///
/// * `path`: The file's absolute path (absolute not in relation to the root file system but `self.base_directory`)
@@ -237,6 +283,15 @@ impl FileSystemRepository for FileSystem {
Ok(file_path)
}
async fn fetch_file(&self, request: FetchFileRequest) -> Result<FileStream, FetchFileError> {
let contents = self
.fetch_file(request.path())
.await
.map_err(|e| anyhow!("Failed to fetch file {}: {e:?}", request.path()))?;
Ok(contents)
}
async fn delete_file(&self, request: DeleteFileRequest) -> Result<FilePath, DeleteFileError> {
let deleted_path = self
.remove_file(request.path())

View File

@@ -52,6 +52,13 @@ impl WarrenMetrics for MetricsDebugLogger {
tracing::debug!("[Metrics] Fetch warrens failed");
}
async fn record_warren_fetch_file_success(&self) {
tracing::debug!("[Metrics] Fetch warren file succeeded");
}
async fn record_warren_fetch_file_failure(&self) {
tracing::debug!("[Metrics] Fetch warren file failed");
}
async fn record_list_warren_files_success(&self) {
tracing::debug!("[Metrics] Warren list files succeeded");
}
@@ -134,6 +141,13 @@ impl FileSystemMetrics for MetricsDebugLogger {
tracing::debug!("[Metrics] File creation failed");
}
async fn record_file_fetch_success(&self) {
tracing::debug!("[Metrics] File fetch succeeded");
}
async fn record_file_fetch_failure(&self) {
tracing::debug!("[Metrics] File fetch failed");
}
async fn record_file_deletion_success(&self) {
tracing::debug!("[Metrics] File deletion succeeded");
}
@@ -255,6 +269,13 @@ impl AuthMetrics for MetricsDebugLogger {
tracing::debug!("[Metrics] User warren deletion failed");
}
async fn record_auth_warren_fetch_file_success(&self) {
tracing::debug!("[Metrics] Warren file fetch succeeded");
}
async fn record_auth_warren_fetch_file_failure(&self) {
tracing::debug!("[Metrics] Warren file fetch failed");
}
async fn record_auth_fetch_user_warren_list_success(&self) {
tracing::debug!("[Metrics] Auth warren list succeeded");
}

View File

@@ -3,7 +3,7 @@ use uuid::Uuid;
use crate::domain::warren::{
models::{
auth_session::requests::FetchAuthSessionResponse,
file::{File, FilePath},
file::{AbsoluteFilePath, File, FilePath},
user::{ListAllUsersAndWarrensResponse, LoginUserResponse, User},
user_warren::UserWarren,
warren::{
@@ -46,6 +46,14 @@ impl WarrenNotifier for NotifierDebugLogger {
tracing::debug!("[Notifier] Fetched warren {}", warren.name());
}
async fn warren_file_fetched(&self, warren: &Warren, path: &AbsoluteFilePath) {
tracing::debug!(
"[Notifier] Fetched file {} in warren {}",
path,
warren.name(),
);
}
async fn warren_files_listed(&self, response: &ListWarrenFilesResponse) {
tracing::debug!(
"[Notifier] Listed {} file(s) in warren {}",
@@ -121,6 +129,10 @@ impl FileSystemNotifier for NotifierDebugLogger {
tracing::debug!("[Notifier] Created file {}", path);
}
async fn file_fetched(&self, path: &AbsoluteFilePath) {
tracing::debug!("[Notifier] Fetched file {path}");
}
async fn file_deleted(&self, path: &FilePath) {
tracing::debug!("[Notifier] Deleted file {}", path);
}
@@ -269,6 +281,18 @@ impl AuthNotifier for NotifierDebugLogger {
);
}
async fn auth_warren_file_fetched(
&self,
user: &User,
warren_id: &Uuid,
path: &AbsoluteFilePath,
) {
tracing::debug!(
"[Notifier] User {} fetched file {path} in warren {warren_id}",
user.id(),
);
}
async fn auth_warren_files_listed(&self, user: &User, response: &ListWarrenFilesResponse) {
tracing::debug!(
"[Notifier] Listed {} file(s) in warren {} for authenticated user {}",