]> uap-core.de Git - note.git/commitdiff
use collection path when creating the note local path
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Mon, 13 Jul 2026 17:09:22 +0000 (19:09 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Mon, 13 Jul 2026 17:09:22 +0000 (19:09 +0200)
application/backend/src/backend.rs
application/backend/src/lib.rs
application/backend/src/storage.rs [new file with mode: 0644]

index f67f855a442941d1b0f9724e28f819c8bddab51c..89d3699389f223f9b02260b42cd3844b9262e69d 100644 (file)
@@ -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<Runtime>,
@@ -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>, 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<String>)>()
-                    .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)),
index d7281c83a19d7214455671339c925c54c3ae4f51..c781e3665d8a00a8d26d1d563b2902429dc16756 100644 (file)
@@ -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 (file)
index 0000000..df6a187
--- /dev/null
@@ -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<Option<String>, 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, 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<Option<String>, 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, 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