]> uap-core.de Git - note.git/commitdiff
generate unique nodename
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 12 Jul 2026 08:53:56 +0000 (10:53 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Sun, 12 Jul 2026 08:53:56 +0000 (10:53 +0200)
Cargo.lock
application/Cargo.toml
application/src/backend.rs
application/src/note.rs
migration/src/m20260502_184134_create_settings.rs

index bcbe107559a9cea3fac525c407c43840701bfc38..498bad92d53010fa552405115a10552d0db128a4 100644 (file)
@@ -468,6 +468,17 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
 
+[[package]]
+name = "chacha20"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.3.0",
+ "rand_core 0.10.1",
+]
+
 [[package]]
 name = "chrono"
 version = "0.4.44"
@@ -586,6 +597,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "cpufeatures"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "crc"
 version = "3.4.0"
@@ -978,6 +998,7 @@ dependencies = [
  "cfg-if",
  "libc",
  "r-efi 6.0.0",
+ "rand_core 0.10.1",
  "wasip2",
  "wasip3",
 ]
@@ -1525,6 +1546,7 @@ dependencies = [
  "dav-rs",
  "entity",
  "migration",
+ "rand 0.10.2",
  "sea-orm",
  "tokio",
  "ui-rs",
@@ -1551,7 +1573,7 @@ dependencies = [
  "num-integer",
  "num-iter",
  "num-traits",
- "rand",
+ "rand 0.8.6",
  "smallvec",
  "zeroize",
 ]
@@ -1934,7 +1956,18 @@ checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
 dependencies = [
  "libc",
  "rand_chacha",
- "rand_core",
+ "rand_core 0.6.4",
+]
+
+[[package]]
+name = "rand"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
+dependencies = [
+ "chacha20",
+ "getrandom 0.4.2",
+ "rand_core 0.10.1",
 ]
 
 [[package]]
@@ -1944,7 +1977,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
 dependencies = [
  "ppv-lite86",
- "rand_core",
+ "rand_core 0.6.4",
 ]
 
 [[package]]
@@ -1956,6 +1989,12 @@ dependencies = [
  "getrandom 0.2.17",
 ]
 
+[[package]]
+name = "rand_core"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
+
 [[package]]
 name = "redox_syscall"
 version = "0.5.18"
@@ -2054,7 +2093,7 @@ dependencies = [
  "num-traits",
  "pkcs1",
  "pkcs8",
- "rand_core",
+ "rand_core 0.6.4",
  "signature",
  "spki",
  "subtle",
@@ -2071,7 +2110,7 @@ dependencies = [
  "borsh",
  "bytes",
  "num-traits",
- "rand",
+ "rand 0.8.6",
  "rkyv",
  "serde",
  "serde_json",
@@ -2396,7 +2435,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
 dependencies = [
  "cfg-if",
- "cpufeatures",
+ "cpufeatures 0.2.17",
  "digest",
 ]
 
@@ -2407,7 +2446,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
 dependencies = [
  "cfg-if",
- "cpufeatures",
+ "cpufeatures 0.2.17",
  "digest",
 ]
 
@@ -2433,7 +2472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
 dependencies = [
  "digest",
- "rand_core",
+ "rand_core 0.6.4",
 ]
 
 [[package]]
@@ -2606,7 +2645,7 @@ dependencies = [
  "memchr",
  "once_cell",
  "percent-encoding",
- "rand",
+ "rand 0.8.6",
  "rsa",
  "rust_decimal",
  "serde",
@@ -2648,7 +2687,7 @@ dependencies = [
  "md-5",
  "memchr",
  "once_cell",
- "rand",
+ "rand 0.8.6",
  "rust_decimal",
  "serde",
  "serde_json",
index af0ace567dde30b2722c70b7894b7e1d6a5d9141..0e5b48086c09013e1815d29cfe2532e3d84da96c 100644 (file)
@@ -10,3 +10,4 @@ entity = { path = "../entity" }
 migration = { path = "../migration" }
 sea-orm = { version = "2.0.0-rc", features = [ "sqlx-sqlite", "sqlx-postgres", "runtime-tokio-native-tls", "macros" ] }
 tokio = { version = "1.52.1", features = ["rt-multi-thread"] }
+rand = "0.10.2"
index 057682e1090561db452a498fdab33c1a6f21e9ce..d9cb8248a7bf55f42ed5c3084eee3bdc7613cef8 100644 (file)
@@ -33,6 +33,8 @@ use sea_orm::{ActiveModelTrait, Database, DatabaseConnection, EntityTrait, Query
 use tokio::runtime::Runtime;
 use std::sync::{Arc};
 use std::thread::JoinHandle;
+use rand::distr::Alphanumeric;
+use rand::RngExt;
 use tokio::sync::{broadcast, mpsc};
 use tokio::sync::broadcast::error::SendError;
 use migration::{Expr, JoinType, Migrator, MigratorTrait};
@@ -391,11 +393,25 @@ impl BackendHandle {
         let _ = self.tx.send(cmd);
     }
     
-    pub fn save_note<F>(&self, initiator: u64, id: NoteId, note: note::ActiveModel, content: Option<notecontent::ActiveModel>, callback: F)
+    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 {
+            callback(SaveNoteResult::ValidationError("collection_id not set"));
+            return;
+        };
+        let Set(nn) = &note.nodename else {
+            callback(SaveNoteResult::ValidationError("nodename not set"));
+            return;
+        };
+        let nodename = nn.clone();
+        let Set(version) = note.version else {
+            callback(SaveNoteResult::ValidationError("version not set"));
+            return;
+        };
+
         let bhandle = self.clone();
         let cmd = Box::pin(async move {
-            let local_storage_path = if let Set(collection_id) = note.collection_id {
+            let local_storage_path = {
                 let result = collection::Entity::find_by_id(collection_id)
                     .join(JoinType::InnerJoin, collection::Relation::Repository.def())
                     .select_only()
@@ -414,8 +430,6 @@ impl BackendHandle {
                 } else {
                     None
                 }
-            } else {
-                return;
             };
 
             let result = if let Set(note_id) = note.note_id {
@@ -427,14 +441,6 @@ impl BackendHandle {
                     update = update.col_expr(Column::Title, Expr::value(title));
                 }
 
-                let version = if let Set(version) = note.version {
-                    version
-                } else {
-                    // not setting version when calling save_note is a mistake and will probably
-                    // lead to a VersionConflict
-                    0
-                };
-
                 let uresult = update
                     .col_expr(Column::Lastmodified, Expr::value(Utc::now()))
                     .col_expr(Column::Version, Expr::col(Column::Version).add(1))
@@ -460,10 +466,27 @@ impl BackendHandle {
                     }
                 }
             } else {
-                let result = note.insert(&bhandle.backend.db).await;
+                let result = note.clone().insert(&bhandle.backend.db).await;
                 match result {
                     Ok(note) => Ok(note),
-                    Err(e) => Err(SaveNoteResult::Error(e)),
+                    Err(e) => {
+                        // The main reason why insertion might fail is the
+                        // collection+name unique constraint.
+                        // Generate a random suffix and try it again
+
+                        let suffix: String = rand::rng()
+                            .sample_iter(Alphanumeric)
+                            .take(6)
+                            .map(char::from)
+                            .collect();
+                        let new_nodename = nodename + "-" + suffix.as_str();
+                        note.nodename = Set(new_nodename);
+                        let result = note.clone().insert(&bhandle.backend.db).await;
+                        match result {
+                            Ok(note) => Ok(note),
+                            Err(e) => Err(SaveNoteResult::Error(e))
+                        }
+                    }
                 }
             };
 
@@ -515,7 +538,8 @@ impl BackendHandle {
 pub enum SaveNoteResult {
     Ok((entity::note::Model, i32)),
     VersionConflict,
-    Error(DbErr)
+    Error(DbErr),
+    ValidationError(&'static str)
 }
 
 impl SaveNoteResult {
index 817a1869305b056f76880965397388bd39e71c79..43f6dadc45b9cdc378b237b185d467bb015efb9c 100644 (file)
@@ -276,6 +276,9 @@ impl Note {
                     },
                     SaveNoteResult::Error(e) => {
                         println!("Failed to save note: {}", e);
+                    },
+                    SaveNoteResult::ValidationError(s) => {
+                        println!("Failed to save note: validation error: {}", s);
                     }
                 }
             });
index 49effd2ba80950a5441ab0b531789980620a8a93..d3c6cf80469f9b025851e73a4cb8ec76b4c2cbd0 100644 (file)
@@ -85,7 +85,7 @@ impl MigrationTrait for Migration {
                 .name("index-note-name")
                 .table("note")
                 .col(Note::CollectionId)
-                .col(Note::NodeName)
+                .col(Note::Nodename)
                 .unique()
                 .to_owned()
         ).await?;
@@ -130,5 +130,5 @@ impl MigrationTrait for Migration {
 #[derive(Iden)]
 enum Note {
     CollectionId,
-    NodeName
+    Nodename
 }
\ No newline at end of file