]> uap-core.de Git - note.git/commitdiff
add support for database-stored file content in open_extern main
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Fri, 17 Jul 2026 16:30:14 +0000 (18:30 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Fri, 17 Jul 2026 16:30:14 +0000 (18:30 +0200)
application/backend/src/backend.rs
application/backend/src/storage.rs
application/note/src/file.rs
entity/src/collection.rs
entity/src/filecontent.rs [new file with mode: 0644]
entity/src/lib.rs
entity/src/note.rs
entity/src/notecontent.rs
entity/src/profile.rs
entity/src/repository.rs
ui-rs/src/ui/toolkit.rs

index 3253a1eb624ccb019a41e197e638a10716640d1e..14823ea8353487927f11da537c899ec4b90a06f1 100644 (file)
@@ -47,7 +47,7 @@ use entity::notecontent::{Entity as NoteContent};
 use migration::prelude::Utc;
 use crate::lockmanager::LockManager;
 use crate::note::{create_nodename, randomize_nodename};
-use crate::storage::{get_collection_storage_path, get_note_storage_path, write_file};
+use crate::storage::{get_collection_storage_path, get_file_content, get_note_storage_path, write_file, write_tmp_file};
 
 pub struct Backend {
     rt: Arc<Runtime>,
@@ -431,7 +431,7 @@ impl BackendHandle {
         });
         let _ = self.tx.send(cmd);
     }
-    
+
     pub fn save_note<F>(&self, initiator: u64, id: NoteId, mut note: note::ActiveModel, content: Option<notecontent::ActiveModel>, callback: F)
     where F: FnOnce(SaveNoteResult) + Send + 'static {
         let Set(collection_id) = note.collection_id else {
@@ -606,9 +606,31 @@ impl BackendHandle {
 
             if let Some(note_path) = note_path {
                 // local storage
-                callback(OpenExternResult::Ok((note_path, false)));
+                callback(OpenExternResult::Ok((PathBuf::from(note_path), false)));
             } else {
-                // TODO: copy content from db to tmp file
+                // get file from database and write it to a tmp file
+                let content_result = get_file_content(&backend.db, note_id).await;
+                match content_result {
+                    Ok(Some(content)) => {
+                        let filename = format!("file_content_{}", content.id);
+                        let path = write_tmp_file(filename.as_str(), content.content).await;
+                        match path {
+                            Ok(path) => {
+                                callback(OpenExternResult::Ok((path, true)));
+                            },
+                            Err(e) => {
+                                callback(OpenExternResult::FsErr(e));
+                            }
+                        }
+
+                    },
+                    Ok(None) => {
+                        callback(OpenExternResult::DbErr(DbErr::Custom("Content not found".into())));
+                    }
+                    Err(e) => {
+                        callback(OpenExternResult::DbErr(e));
+                    }
+                }
             }
         });
         let _ = self.tx.send(cmd);
@@ -627,7 +649,7 @@ pub enum SaveNoteResult {
 }
 
 pub enum OpenExternResult {
-    Ok((String, bool)), // path, istmp
+    Ok((PathBuf, bool)), // path, istmp
     DbErr(DbErr),
     FsErr(Error)
 }
index eb714451bb9f53a368d34e282e14d154907ca3ea..57ff85441e1b931d15d6fb276684b508a54a0416 100644 (file)
 
 use std::io::ErrorKind;
 use std::path::Path;
-use sea_orm::{DatabaseConnection, DbErr, EntityTrait, QuerySelect, RelationTrait};
+use sea_orm::entity::prelude::*;
+use sea_orm::{ColumnTrait, DatabaseConnection, DbErr, EntityTrait, QuerySelect, RelationTrait};
 use tokio::fs;
 use tokio::fs::File;
 use tokio::io::AsyncWriteExt;
-use entity::{collection, note, repository};
+use entity::{collection, filecontent, note, repository};
 use entity::collection::LocalStorageSetting;
+use entity::filecontent::Model as FileContent;
 use migration::JoinType;
 
 /// Checks if a collection should use local storage
@@ -92,6 +94,12 @@ pub async fn get_collection_storage_path(db: &DatabaseConnection, collection_id:
     }
 }
 
+pub async fn get_file_content(db: &DatabaseConnection, note_id: i32) -> Result<Option<FileContent>, DbErr> {
+    filecontent::Entity::find()
+        .filter(filecontent::Column::NoteId.eq(note_id))
+        .one(db).await
+}
+
 pub async fn write_file(path: &Path, content: &str) -> std::io::Result<()> {
     let mut file = match File::create(path).await {
         Ok(f) => f,
@@ -106,4 +114,14 @@ pub async fn write_file(path: &Path, content: &str) -> std::io::Result<()> {
 
     file.write_all(content.as_bytes()).await?;
     Ok(())
-}
\ No newline at end of file
+}
+
+pub async fn write_tmp_file(name: &str, data: Vec<u8>) -> std::io::Result<std::path::PathBuf> {
+    let path = std::env::temp_dir().join(name);
+
+    let mut file = File::create(&path).await?;
+    file.write_all(&data).await?;
+    file.flush().await?;
+
+    Ok(path)
+}
index 3a9df0f77f1439c7ed02afbbebf250c4edb1a0e0..d2c1df92e5fc2adea4dbaadb18e6aedf3abe27f9 100644 (file)
@@ -25,7 +25,7 @@
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  * POSSIBILITY OF SUCH DAMAGE.
  */
-
+use std::path::PathBuf;
 use std::rc::Rc;
 use backend::backend::{BackendHandle, NoteId, OpenExternResult};
 use ui_rs::{action, doc_cast, ui_actions, UiModel};
@@ -44,7 +44,7 @@ pub struct FileNote {
     pub id: NoteId,
     pub nodename: String,
 
-    pub local_path: Option<String>,
+    pub local_path: Option<PathBuf>,
     pub is_tmp_file: bool,
     pub is_opening: bool,
 
@@ -97,8 +97,8 @@ impl FileNote {
         };
 
         if let Some(path) = &self.local_path {
-            println!("open file: {}", path);
-            open_file(path.as_str());
+            println!("open file: {}", path.to_string_lossy());
+            open_file(path.to_string_lossy().as_ref());
         } else if let NoteId::Id(note_id) = self.id {
             self.is_opening = true;
             let proxy = doc.doc_proxy();
@@ -107,8 +107,8 @@ impl FileNote {
                     note.is_opening = false;
                     match result {
                         OpenExternResult::Ok((path, istmp)) => {
-                            println!("open file: {}", path);
-                            open_file(path.as_str());
+                            println!("open file: {}", path.to_string_lossy());
+                            open_file(path.to_string_lossy().as_ref());
                             note.local_path = Some(path);
                             note.is_tmp_file = istmp;
                         }
index 858ebd24385c07e1dbc190b967c4b1fe888f0603..20fd647dd41964653021f31cbb5036ca9dd2e91a 100644 (file)
@@ -1,3 +1,31 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
 use std::collections::HashMap;
 use sea_orm::entity::prelude::*;
 
diff --git a/entity/src/filecontent.rs b/entity/src/filecontent.rs
new file mode 100644 (file)
index 0000000..4630431
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+use sea_orm::entity::prelude::*;
+use sea_orm::{ActiveModelBehavior, DeriveEntityModel, DeriveRelation, EnumIter};
+
+#[sea_orm::model]
+#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
+#[sea_orm(table_name = "file_content")]
+pub struct Model {
+    #[sea_orm(primary_key)]
+    pub id: i32,
+
+    #[sea_orm(unique)]
+    pub note_id: i32,
+
+    pub content: Vec<u8>,
+
+    #[sea_orm(belongs_to, from = "note_id", to = "note_id")]
+    pub note: HasOne<crate::note::Entity>
+}
+
+impl ActiveModelBehavior for ActiveModel {}
index 1b9a69462b98eb9cfd5fd34acea02776702e7b26..0fc36c533843d16b1224d55860cd7a1f19f0feaf 100644 (file)
@@ -3,4 +3,5 @@ pub mod profile;
 pub mod collection;
 pub mod note;
 pub mod notecontent;
-pub mod repository;
\ No newline at end of file
+pub mod repository;
+pub mod filecontent;
\ No newline at end of file
index 9cf3278e99eab4d5b7f8cd0abf262d9c8d04798f..8df1f1ac69dff4bbaabdbda3e32d703d530a0528 100644 (file)
@@ -1,5 +1,32 @@
 use sea_orm::entity::prelude::*;
 
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
 
 #[sea_orm::model]
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
@@ -19,9 +46,6 @@ pub struct Model {
     #[sea_orm(belongs_to, from = "collection_id", to = "collection_id")]
     pub repository: HasOne<super::collection::Entity>,
 
-    #[sea_orm(has_one)]
-    pub content: HasOne<crate::notecontent::Entity>,
-
     pub version: i64
 }
 
index 0396da172e00945269c7dc870e20cc91727ed5e7..8bf7841d1e38f2195daa56441f29a4c8e3bac7c5 100644 (file)
@@ -1,3 +1,31 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
 use sea_orm::entity::prelude::*;
 
 use sea_orm::{ActiveModelBehavior, DeriveEntityModel, DeriveRelation, EnumIter};
index 660ebc2af9f496a594b9611d467bb0a599ee808c..ec70965b0aca112c3357cc803fb372f080c472cf 100644 (file)
@@ -1,3 +1,31 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
 use sea_orm::entity::prelude::*;
 
 #[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
index 17ed22bd39743cb6a6296a8ed37000af4e4ee6b4..b7df46e1c339e98709bad3df0b1c1f0f5a371091 100644 (file)
@@ -1,3 +1,31 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
 use sea_orm::entity::prelude::*;
 
 
index 5012b571b46568e3ceab65b5666f31967d48632f..001dd9be05a783dab8c49fe3f4c64822a0586e2b 100644 (file)
@@ -296,7 +296,7 @@ impl<T: ?Sized + UiModel + UiActions> UiDoc<T> {
     pub fn new_from_box2<F>(data: Box<T>, init: F) -> UiDoc<T>
     where F: FnOnce(&mut T, &UiDoc<T>) {
         unsafe {
-            let doc = ui_document_new(10000 + mem::size_of::<*mut Box<T>>());
+            let doc = ui_document_new(mem::size_of::<*mut Box<T>>());
             let ctx = UiContext::from_ptr(ui_document_context(doc));
             ui_app_ref();
             ui_reg_destructor(ctx.ptr, std::ptr::null_mut(), doc_app_unref);