From: Olaf Wintermann Date: Mon, 13 Jul 2026 17:09:22 +0000 (+0200) Subject: use collection path when creating the note local path X-Git-Url: https://uap-core.de/gitweb/?a=commitdiff_plain;h=1cd4dd60e3df8067d18fbf43f736b279d37b38e3;p=note.git use collection path when creating the note local path --- diff --git a/application/backend/src/backend.rs b/application/backend/src/backend.rs index f67f855..89d3699 100644 --- a/application/backend/src/backend.rs +++ b/application/backend/src/backend.rs @@ -30,26 +30,25 @@ use std::future::Future; use std::io::Error; use std::path::{Path, PathBuf}; use std::pin::Pin; -use sea_orm::{ActiveModelTrait, Database, DatabaseConnection, EntityTrait, QueryFilter, ColumnTrait, Set, QueryOrder, DbErr, ExprTrait, RelationTrait, QuerySelect}; +use sea_orm::{ActiveModelTrait, Database, DatabaseConnection, EntityTrait, QueryFilter, ColumnTrait, Set, QueryOrder, DbErr, ExprTrait}; use tokio::runtime::Runtime; use std::sync::{Arc}; use std::thread::JoinHandle; use rand::distr::Alphanumeric; use rand::RngExt; use tokio::fs; -use tokio::fs::File; -use tokio::io::AsyncWriteExt; use tokio::sync::{broadcast, mpsc}; use tokio::sync::broadcast::error::SendError; -use migration::{Expr, JoinType, Migrator, MigratorTrait}; +use migration::{Expr, Migrator, MigratorTrait}; use entity::{collection, note, notecontent, profile, repository}; use entity::profile::Entity as Profile; -use entity::collection::{create_notebook_hierarchy, CollectionType, Entity as Collection, LocalStorageSetting, Node}; +use entity::collection::{create_notebook_hierarchy, CollectionType, Entity as Collection, Node}; use entity::note::{Column, Entity as Note}; use entity::notecontent::{Entity as NoteContent}; use migration::prelude::Utc; use crate::lockmanager::LockManager; +use crate::storage::{get_collection_storage_path, get_note_storage_path, write_file}; pub struct Backend { rt: Arc, @@ -388,38 +387,19 @@ impl BackendHandle { let backend = self.backend.clone(); let cmd = Box::pin(async move { // check if the note content is stored in the filesystem or database - let storage_result = note::Entity::find_by_id(note_id) - .join(JoinType::InnerJoin, note::Relation::Collection.def()) - .join(JoinType::InnerJoin, collection::Relation::Repository.def()) - .select_only() - .column(collection::Column::Storage) - .column(repository::Column::UseLocalStorage) - .column(repository::Column::LocalPath) - .column(note::Column::Nodename) - .into_tuple::<(LocalStorageSetting, bool, Option, String)>() - .one(&backend.db).await; - let storage = match storage_result { - Ok(storage) => { - if let Some(storage) = storage { - storage - } else { - // Should not happen, a note always has a parent collection, which - // also always is assigned to a repository - return; - } - }, + let storage_result = get_note_storage_path(&backend.db, note_id).await; + let note_path = match storage_result { + Ok(note_path) => note_path, Err(e) => { callback(Err(e)); return; } }; - if let Some(dir) = storage.2 && - (storage.0 == LocalStorageSetting::FileSystem || (storage.0 == LocalStorageSetting::Default && storage.1)) - { + if let Some(note_path) = note_path { // local storage - let path = Path::new(dir.as_str()).join(storage.3); - let content = fs::read_to_string(path.as_path()).await; + let path = Path::new(note_path.as_str()); + let content = fs::read_to_string(path).await; match content { Ok(content) => { let model = notecontent::Model { @@ -437,7 +417,7 @@ impl BackendHandle { }, */ - Err(e) => { + Err(_e) => { let msg = format!("Cannot open file {}", path.display()); callback(Err(DbErr::Custom(msg))); } @@ -478,29 +458,13 @@ impl BackendHandle { let bhandle = self.clone(); let cmd = Box::pin(async move { let local_storage_path = { - let result = collection::Entity::find_by_id(collection_id) - .join(JoinType::InnerJoin, collection::Relation::Repository.def()) - .select_only() - .column(collection::Column::Storage) - .column(repository::Column::UseLocalStorage) - .column(repository::Column::LocalPath) - .into_tuple::<(LocalStorageSetting, bool, Option)>() - .one(&bhandle.backend.db).await; - let storage = if let Ok(okres) = result && let Some(storage) = okres { - storage - } else { - // should not happen - callback(SaveNoteResult::Error(DbErr::Custom("Cannot get storage settings".to_string()))); - return; - }; - - if storage.0 == LocalStorageSetting::FileSystem || (storage.0 == LocalStorageSetting::Default && storage.1) { - if storage.2.is_none() { - println!("Warning: collection is configured to use local storage, but there is no local storage path configured in the repository"); + let result = get_collection_storage_path(&bhandle.backend.db, collection_id).await; + match result { + Ok(path) => path, + Err(e) => { + callback(SaveNoteResult::Error(e)); + return; } - storage.2 - } else { - None } }; @@ -627,11 +591,7 @@ impl BackendHandle { } } -async fn write_file(path: &Path, content: &str) -> std::io::Result<()> { - let mut file = File::create(path).await?; - file.write_all(content.as_bytes()).await?; - Ok(()) -} + pub enum SaveNoteResult { Ok((entity::note::Model, i32)), diff --git a/application/backend/src/lib.rs b/application/backend/src/lib.rs index d7281c8..c781e36 100644 --- a/application/backend/src/lib.rs +++ b/application/backend/src/lib.rs @@ -1,2 +1,3 @@ pub mod backend; -pub mod lockmanager; \ No newline at end of file +pub mod lockmanager; +pub mod storage; \ No newline at end of file diff --git a/application/backend/src/storage.rs b/application/backend/src/storage.rs new file mode 100644 index 0000000..df6a187 --- /dev/null +++ b/application/backend/src/storage.rs @@ -0,0 +1,81 @@ +use std::io::ErrorKind; +use std::path::Path; +use sea_orm::{DatabaseConnection, DbErr, EntityTrait, QuerySelect, RelationTrait}; +use tokio::fs; +use tokio::fs::File; +use tokio::io::AsyncWriteExt; +use entity::{collection, note, repository}; +use entity::collection::LocalStorageSetting; +use migration::JoinType; + +/// Checks if a collection should use local storage +fn uses_local_storage(settings: LocalStorageSetting, repo_use_local: bool) -> bool { + settings == LocalStorageSetting::FileSystem || (settings == LocalStorageSetting::Default && repo_use_local) +} + +pub async fn get_note_storage_path(db: &DatabaseConnection, note_id: i32) -> Result, DbErr> { + // check if the note content is stored in the filesystem or database + let result = note::Entity::find_by_id(note_id) + .join(JoinType::InnerJoin, note::Relation::Collection.def()) + .join(JoinType::InnerJoin, collection::Relation::Repository.def()) + .select_only() + .column(collection::Column::Storage) + .column(repository::Column::UseLocalStorage) + .column(repository::Column::LocalPath) + .column(collection::Column::Parent) + .column(collection::Column::Name) + .column(note::Column::Nodename) + .into_tuple::<(LocalStorageSetting, bool, Option, String, String, String)>() + .one(db).await?; + + let Some(storage) = result else { + return Err(DbErr::RecordNotFound("Cannot find storage settings".to_string())); + }; + + if let Some(local_path) = storage.2 && uses_local_storage(storage.0, storage.1) { + let collection_path = format!("{}/{}/{}/{}", local_path, storage.3, storage.4, storage.5); + Ok(Some(collection_path)) + } else { + Ok(None) + } +} + +pub async fn get_collection_storage_path(db: &DatabaseConnection, collection_id: i32) -> Result, DbErr> { + let result = collection::Entity::find_by_id(collection_id) + .join(JoinType::InnerJoin, collection::Relation::Repository.def()) + .select_only() + .column(collection::Column::Storage) + .column(repository::Column::UseLocalStorage) + .column(repository::Column::LocalPath) + .column(collection::Column::Parent) + .column(collection::Column::Name) + .into_tuple::<(LocalStorageSetting, bool, Option, String, String)>() + .one(db).await?; + + let Some(storage) = result else { + return Err(DbErr::RecordNotFound("Cannot find storage settings".to_string())); + }; + + if let Some(local_path) = storage.2 && uses_local_storage(storage.0, storage.1) { + let collection_path = format!("{}/{}/{}", local_path, storage.3, storage.4); + Ok(Some(collection_path)) + } else { + Ok(None) + } +} + +pub async fn write_file(path: &Path, content: &str) -> std::io::Result<()> { + let mut file = match File::create(path).await { + Ok(f) => f, + Err(err) if err.kind() == ErrorKind::NotFound => { + if let Some(parent) = path.parent() { + fs::create_dir_all(parent).await?; + } + File::create(path).await? + } + Err(err) => return Err(err), + }; + + file.write_all(content.as_bytes()).await?; + Ok(()) +} \ No newline at end of file