diff --git a/package.json b/package.json index d7802c1..8c117ef 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "@vue/compiler-sfc": "^3.5.17", "drizzle-kit": "^0.31.2", "globals": "^16.2.0", + "prettier": "3.6.2", "typescript": "^5.8.3" }, "packageManager": "pnpm@10.12.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0968ebf..a7b2be9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -123,6 +123,9 @@ importers: globals: specifier: ^16.2.0 version: 16.2.0 + prettier: + specifier: 3.6.2 + version: 3.6.2 typescript: specifier: ^5.8.3 version: 5.8.3 @@ -4374,6 +4377,11 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + pretty-bytes@6.1.1: resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} engines: {node: ^14.13.1 || >=16.0.0} @@ -10003,6 +10011,8 @@ snapshots: prelude-ls@1.2.1: {} + prettier@3.6.2: {} + pretty-bytes@6.1.1: {} process-nextick-args@2.0.1: {} diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 0b2bd60..12a6830 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -285,6 +285,15 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -342,9 +351,9 @@ dependencies = [ [[package]] name = "brotli" -version = "7.0.0" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -353,9 +362,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -665,15 +674,15 @@ dependencies = [ [[package]] name = "cssparser" -version = "0.27.2" +version = "0.29.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" +checksum = "f93d03419cb5950ccfd3daf3ff1c7a36ace64609a1a8746d493df1ca0afde0fa" dependencies = [ "cssparser-macros", "dtoa-short", - "itoa 0.4.8", + "itoa", "matches", - "phf 0.8.0", + "phf 0.10.1", "proc-macro2", "quote", "smallvec", @@ -1545,12 +1554,14 @@ dependencies = [ "sqlparser", "tauri", "tauri-build", + "tauri-plugin-android-fs", "tauri-plugin-dialog", "tauri-plugin-fs", "tauri-plugin-http", "tauri-plugin-notification", "tauri-plugin-opener", "tauri-plugin-os", + "tauri-plugin-persisted-scope", "tauri-plugin-store", "tokio", "uhlc", @@ -1606,16 +1617,14 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "html5ever" -version = "0.26.0" +version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +checksum = "3b7410cae13cbc75623c98ac4cbfd1f0bedddf3227afc24f370cf0f50a44a11c" dependencies = [ "log", "mac", "markup5ever", - "proc-macro2", - "quote", - "syn 1.0.109", + "match_token", ] [[package]] @@ -1626,7 +1635,7 @@ checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", - "itoa 1.0.15", + "itoa", ] [[package]] @@ -1683,7 +1692,7 @@ dependencies = [ "http", "http-body", "httparse", - "itoa 1.0.15", + "itoa", "pin-project-lite", "smallvec", "tokio", @@ -1936,6 +1945,17 @@ dependencies = [ "cfb", ] +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags 2.9.0", + "cfg-if", + "libc", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -1961,12 +1981,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - [[package]] name = "itoa" version = "1.0.15" @@ -2063,14 +2077,13 @@ dependencies = [ [[package]] name = "kuchikiki" -version = "0.8.2" +version = "0.8.8-speedreader" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e4755b7b995046f510a7520c42b2fed58b77bd94d5a87a8eb43d2fd126da8" +checksum = "02cb977175687f33fa4afa0c95c112b987ea1443e5a51c8f8ff27dc618270cc2" dependencies = [ "cssparser", "html5ever", - "indexmap 1.9.3", - "matches", + "indexmap 2.8.0", "selectors", ] @@ -2132,9 +2145,9 @@ dependencies = [ [[package]] name = "libsqlite3-sys" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91632f3b4fb6bd1d72aa3d78f41ffecfcf2b1a6648d8c241dbe7dbfaf4875e15" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" dependencies = [ "cc", "openssl-sys", @@ -2202,18 +2215,29 @@ dependencies = [ [[package]] name = "markup5ever" -version = "0.11.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" dependencies = [ "log", - "phf 0.10.1", - "phf_codegen 0.10.0", + "phf 0.11.3", + "phf_codegen 0.11.3", "string_cache", "string_cache_codegen", "tendril", ] +[[package]] +name = "match_token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.100", +] + [[package]] name = "matches" version = "0.1.10" @@ -2274,9 +2298,9 @@ dependencies = [ [[package]] name = "muda" -version = "0.16.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492" +checksum = "58b89bf91c19bf036347f1ab85a81c560f08c0667c8601bece664d860a600988" dependencies = [ "crossbeam-channel", "dpi", @@ -2758,9 +2782,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" dependencies = [ - "phf_macros 0.8.0", "phf_shared 0.8.0", - "proc-macro-hack", ] [[package]] @@ -2769,7 +2791,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" dependencies = [ + "phf_macros 0.10.0", "phf_shared 0.10.0", + "proc-macro-hack", ] [[package]] @@ -2794,12 +2818,12 @@ dependencies = [ [[package]] name = "phf_codegen" -version = "0.10.0" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator 0.10.0", - "phf_shared 0.10.0", + "phf_generator 0.11.3", + "phf_shared 0.11.3", ] [[package]] @@ -2834,12 +2858,12 @@ dependencies = [ [[package]] name = "phf_macros" -version = "0.8.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", + "phf_generator 0.10.0", + "phf_shared 0.10.0", "proc-macro-hack", "proc-macro2", "quote", @@ -3433,9 +3457,9 @@ dependencies = [ [[package]] name = "rusqlite" -version = "0.36.0" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3de23c3319433716cf134eed225fe9986bc24f63bed9be9f20c329029e672dc7" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ "bitflags 2.9.0", "fallible-iterator", @@ -3591,22 +3615,20 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "selectors" -version = "0.22.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" +checksum = "0c37578180969d00692904465fb7f6b3d50b9a2b952b87c23d0e2e5cb5013416" dependencies = [ "bitflags 1.3.2", "cssparser", "derive_more", "fxhash", "log", - "matches", "phf 0.8.0", "phf_codegen 0.8.0", "precomputed-hash", "servo_arc", "smallvec", - "thin-slice", ] [[package]] @@ -3666,7 +3688,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "itoa 1.0.15", + "itoa", "memchr", "ryu", "serde", @@ -3699,7 +3721,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.15", + "itoa", "ryu", "serde", ] @@ -3758,9 +3780,9 @@ dependencies = [ [[package]] name = "servo_arc" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" +checksum = "d52aa42f8fdf0fed91e5ce7f23d8138441002fa31dca008acf47e6fd4721f741" dependencies = [ "nodrop", "stable_deref_trait", @@ -4071,9 +4093,9 @@ dependencies = [ [[package]] name = "tao" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e59c1f38e657351a2e822eadf40d6a2ad4627b9c25557bc1180ec1b3295ef82" +checksum = "49c380ca75a231b87b6c9dd86948f035012e7171d1a7c40a9c2890489a7ffd8a" dependencies = [ "bitflags 2.9.0", "core-foundation 0.10.0", @@ -4127,17 +4149,16 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.5.1" +version = "2.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7b0bc1aec81bda6bc455ea98fcaed26b3c98c1648c627ad6ff1c704e8bf8cbc" +checksum = "124e129c9c0faa6bec792c5948c89e86c90094133b0b9044df0ce5f0a8efaa0d" dependencies = [ "anyhow", "bytes", "dirs", "dunce", "embed_plist", - "futures-util", - "getrandom 0.2.15", + "getrandom 0.3.2", "glob", "gtk", "heck 0.5.0", @@ -4179,9 +4200,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a0350f0df1db385ca5c02888a83e0e66655c245b7443db8b78a70da7d7f8fc" +checksum = "12f025c389d3adb83114bec704da973142e82fc6ec799c7c750c5e21cefaec83" dependencies = [ "anyhow", "cargo_toml", @@ -4201,9 +4222,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93f035551bf7b11b3f51ad9bc231ebbe5e085565527991c16cf326aa38cdf47" +checksum = "f5df493a1075a241065bc865ed5ef8d0fbc1e76c7afdc0bf0eccfaa7d4f0e406" dependencies = [ "base64 0.22.1", "brotli", @@ -4228,9 +4249,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db4df25e2d9d45de0c4c910da61cd5500190da14ae4830749fee3466dddd112" +checksum = "f237fbea5866fa5f2a60a21bea807a2d6e0379db070d89c3a10ac0f2d4649bbc" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -4242,9 +4263,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9972871fcbddf16618f70412d965d4d845cd4b76d03fff168709961ef71e5cdf" +checksum = "1d9a0bd00bf1930ad1a604d08b0eb6b2a9c1822686d65d7f4731a7723b8901d3" dependencies = [ "anyhow", "glob", @@ -4258,10 +4279,25 @@ dependencies = [ ] [[package]] -name = "tauri-plugin-dialog" -version = "2.2.0" +name = "tauri-plugin-android-fs" +version = "9.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b59fd750551b1066744ab956a1cd6b1ea3e1b3763b0b9153ac27a044d596426" +checksum = "70913be3272e29ada966e8158ffb4f1b3985041635bf4563baade6178309231a" +dependencies = [ + "percent-encoding", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.12", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aefb14219b492afb30b12647b5b1247cadd2c0603467310c36e0f7ae1698c28" dependencies = [ "log", "raw-window-handle", @@ -4277,9 +4313,9 @@ dependencies = [ [[package]] name = "tauri-plugin-fs" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ead0daec5d305adcefe05af9d970fc437bcc7996052d564e7393eb291252da" +checksum = "c341290d31991dbca38b31d412c73dfbdb070bb11536784f19dd2211d13b778f" dependencies = [ "anyhow", "dunce", @@ -4299,10 +4335,12 @@ dependencies = [ [[package]] name = "tauri-plugin-http" -version = "2.4.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "696ef548befeee6c6c17b80ef73e7c41205b6c2204e87ef78ccc231212389a5c" +checksum = "b0c1a38da944b357ffa23bafd563b1579f18e6fbd118fcd84769406d35dcc5c7" dependencies = [ + "bytes", + "cookie_store", "data-url", "http", "regex", @@ -4321,9 +4359,9 @@ dependencies = [ [[package]] name = "tauri-plugin-notification" -version = "2.2.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c474c7cc524385e682ccc1e149e13913a66fd8586ac4c2319cf01b78f070d309" +checksum = "cfe06ed89cff6d0ec06ff4f544fb961e4718348a33309f56ccb2086e77bc9116" dependencies = [ "log", "notify-rust", @@ -4340,9 +4378,9 @@ dependencies = [ [[package]] name = "tauri-plugin-opener" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8983f50326d34437142a6d560b5c3426e91324297519b6eeb32ed0a1d1e0f2" +checksum = "ecee219f11cdac713ab32959db5d0cceec4810ba4f4458da992292ecf9660321" dependencies = [ "dunce", "glob", @@ -4362,9 +4400,9 @@ dependencies = [ [[package]] name = "tauri-plugin-os" -version = "2.2.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "424f19432397850c2ddd42aa58078630c15287bbce3866eb1d90e7dbee680637" +checksum = "05bccb4c6de4299beec5a9b070878a01bce9e2c945aa7a75bcea38bcba4c675d" dependencies = [ "gethostname", "log", @@ -4379,10 +4417,26 @@ dependencies = [ ] [[package]] -name = "tauri-plugin-store" -version = "2.2.0" +name = "tauri-plugin-persisted-scope" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c0c08fae6995909f5e9a0da6038273b750221319f2c0f3b526d6de1cde21505" +checksum = "7380eff2525adcf7f6b1cf3de191ccd3fdbe2a42281e4659604a26749c77bfbd" +dependencies = [ + "aho-corasick", + "bincode", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin-fs", + "thiserror 2.0.12", +] + +[[package]] +name = "tauri-plugin-store" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5916c609664a56c82aeaefffca9851fd072d4d41f73d63f22ee3ee451508194f" dependencies = [ "dunce", "serde", @@ -4396,9 +4450,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f004905d549854069e6774533d742b03cacfd6f03deb08940a8677586cbe39" +checksum = "9e7bb73d1bceac06c20b3f755b2c8a2cb13b20b50083084a8cf3700daf397ba4" dependencies = [ "cookie", "dpi", @@ -4418,9 +4472,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f85d056f4d4b014fe874814034f3416d57114b617a493a4fe552580851a3f3a2" +checksum = "902b5aa9035e16f342eb64f8bf06ccdc2808e411a2525ed1d07672fa4e780bad" dependencies = [ "gtk", "http", @@ -4445,9 +4499,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2900399c239a471bcff7f15c4399eb1a8c4fe511ba2853e07c996d771a5e0a4" +checksum = "41743bbbeb96c3a100d234e5a0b60a46d5aa068f266160862c7afdbf828ca02e" dependencies = [ "anyhow", "brotli", @@ -4527,12 +4581,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "thin-slice" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" - [[package]] name = "thiserror" version = "1.0.69" @@ -4580,7 +4628,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d9c75b47bdff86fa3334a3db91356b8d7d86a9b839dab7d0bdc5c3d3a077618" dependencies = [ "deranged", - "itoa 1.0.15", + "itoa", "num-conv", "powerfmt", "serde", @@ -4631,16 +4679,18 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.0" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", "tracing", @@ -4797,9 +4847,9 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d433764348e7084bad2c5ea22c96c71b61b17afe3a11645710f533bd72b6a2b5" +checksum = "2da75ec677957aa21f6e0b361df0daab972f13a5bee3606de0638fd4ee1c666a" dependencies = [ "crossbeam-channel", "dirs", @@ -5214,9 +5264,9 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b542b5cfbd9618c46c2784e4d41ba218c336ac70d44c55e47b251033e7d85601" +checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4" dependencies = [ "webview2-com-macros", "webview2-com-sys", @@ -5239,9 +5289,9 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.37.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae2d11c4a686e4409659d7891791254cf9286d3cfe0eef54df1523533d22295" +checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c" dependencies = [ "thiserror 2.0.12", "windows", @@ -5762,9 +5812,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.51.2" +version = "0.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886a0a9d2a94fd90cfa1d929629b79cfefb1546e2c7430c63a47f0664c0e4e2" +checksum = "12a714d9ba7075aae04a6e50229d6109e3d584774b99a6a8c60de1698ca111b9" dependencies = [ "base64 0.22.1", "block2 0.6.0", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 5a181a8..d720ac5 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -18,14 +18,14 @@ crate-type = ["staticlib", "cdylib", "rlib"] tauri-build = { version = "2.2", features = [] } [dependencies] -rusqlite = { version = "0.36.0", features = [ +rusqlite = { version = "0.37.0", features = [ "load_extension", "bundled-sqlcipher-vendored-openssl", "functions", ] } #libsqlite3-sys = { version = "0.31", features = ["bundled-sqlcipher"] } #sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "sqlite"] } -tokio = { version = "1.45", features = ["macros", "rt-multi-thread"] } +tokio = { version = "1.46", features = ["macros", "rt-multi-thread"] } serde = { version = "1", features = ["derive"] } hex = "0.4" serde_json = "1" @@ -35,12 +35,16 @@ mime = "0.3" fs_extra = "1.3.0" sqlparser = { version = "0.57.0", features = ["visitor"] } uhlc = "0.8" -tauri = { version = "2.5", features = ["protocol-asset", "devtools"] } -tauri-plugin-dialog = "2.2" -tauri-plugin-fs = "2.3.0" -tauri-plugin-opener = "2.3.0" -tauri-plugin-os = "2" -tauri-plugin-store = "2" -tauri-plugin-http = "2.4" -tauri-plugin-notification = "2" +tauri = { version = "2.6.2", features = ["protocol-asset", "devtools"] } +tauri-plugin-dialog = "2.3" +tauri-plugin-fs = "2.4.0" +tauri-plugin-opener = "2.4.0" +tauri-plugin-os = "2.3" +tauri-plugin-store = "2.3" +tauri-plugin-http = "2.5" +tauri-plugin-notification = "2.3" +tauri-plugin-persisted-scope = "2.0.0" +tauri-plugin-android-fs = "9.5.0" #tauri-plugin-sql = { version = "2", features = ["sqlite"] } + + diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 6019833..2df7842 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -19,8 +19,17 @@ "fs:allow-appdata-read-recursive", "fs:allow-appdata-write-recursive", "fs:allow-read-file", + "fs:allow-read-dir", "fs:allow-resource-read-recursive", + "fs:allow-resource-write-recursive", + "fs:allow-download-read-recursive", + "fs:allow-download-write-recursive", "fs:default", + "android-fs:default", + { + "identifier": "fs:scope", + "allow": [{ "path": "**" }] + }, "http:allow-fetch-send", "http:allow-fetch", "http:default", diff --git a/src-tauri/gen/android/app/build.gradle.kts b/src-tauri/gen/android/app/build.gradle.kts index 387fd93..0101ac9 100644 --- a/src-tauri/gen/android/app/build.gradle.kts +++ b/src-tauri/gen/android/app/build.gradle.kts @@ -19,7 +19,7 @@ android { defaultConfig { manifestPlaceholders["usesCleartextTraffic"] = "false" applicationId = "space.haex.hub" - minSdk = 24 + minSdk = 30 targetSdk = 34 versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt() versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0") diff --git a/src-tauri/gen/android/app/src/main/AndroidManifest.xml b/src-tauri/gen/android/app/src/main/AndroidManifest.xml index b6a3941..bffd799 100644 --- a/src-tauri/gen/android/app/src/main/AndroidManifest.xml +++ b/src-tauri/gen/android/app/src/main/AndroidManifest.xml @@ -1,7 +1,9 @@ - + + + @@ -9,7 +11,8 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/Theme.haex_hub" - android:usesCleartextTraffic="${usesCleartextTraffic}"> + android:usesCleartextTraffic="${usesCleartextTraffic}" + android:requestLegacyExternalStorage="true"> - + android:name="androidx.core.content.FileProvider" + android:authorities="${applicationId}.fileprovider" + android:exported="false" + android:grantUriPermissions="true"> + - + \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/assets/database/vault.db b/src-tauri/gen/android/app/src/main/assets/database/vault.db index 8e9d1b4..d66fa61 100644 Binary files a/src-tauri/gen/android/app/src/main/assets/database/vault.db and b/src-tauri/gen/android/app/src/main/assets/database/vault.db differ diff --git a/src-tauri/gen/android/app/src/main/assets/resources/vault.db b/src-tauri/gen/android/app/src/main/assets/resources/vault.db deleted file mode 100644 index 7dfe673..0000000 Binary files a/src-tauri/gen/android/app/src/main/assets/resources/vault.db and /dev/null differ diff --git a/src-tauri/gen/android/app/src/main/java/space/haex/hub/MainActivity.kt b/src-tauri/gen/android/app/src/main/java/space/haex/hub/MainActivity.kt index cee843f..9edff4a 100644 --- a/src-tauri/gen/android/app/src/main/java/space/haex/hub/MainActivity.kt +++ b/src-tauri/gen/android/app/src/main/java/space/haex/hub/MainActivity.kt @@ -1,3 +1,20 @@ package space.haex.hub +import android.os.Build +import android.os.Bundle +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.provider.Settings -class MainActivity : TauriActivity() \ No newline at end of file +class MainActivity : TauriActivity(){ + + /* override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + startActivity( + Intent( + Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION, + Uri.parse("package:${BuildConfig.APPLICATION_ID}") + ) + ) + } */ +} \ No newline at end of file diff --git a/src-tauri/gen/android/app/src/main/java/space/haex/hub/SettingsOpener.kt b/src-tauri/gen/android/app/src/main/java/space/haex/hub/SettingsOpener.kt new file mode 100644 index 0000000..c9acc2c --- /dev/null +++ b/src-tauri/gen/android/app/src/main/java/space/haex/hub/SettingsOpener.kt @@ -0,0 +1,17 @@ +package space.haex.hub + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.provider.Settings + +class SettingsOpener { + companion object { + @JvmStatic + fun openManageAllFilesAccessSettings(context: Context) { + val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) + intent.data = Uri.parse("package:" + context.packageName) + context.startActivity(intent) + } + } +} \ No newline at end of file diff --git a/src-tauri/resources/crsqlite b/src-tauri/resources/crsqlite deleted file mode 100755 index 22c3157..0000000 Binary files a/src-tauri/resources/crsqlite and /dev/null differ diff --git a/src-tauri/src/android_storage/mod.rs b/src-tauri/src/android_storage/mod.rs new file mode 100644 index 0000000..ca5629d --- /dev/null +++ b/src-tauri/src/android_storage/mod.rs @@ -0,0 +1,94 @@ +#[cfg(target_os = "android")] +#[tauri::command] +pub async fn request_storage_permission(app_handle: tauri::AppHandle) -> Result { + Ok("Settings opened - Enable 'Allow management of all files'".to_string()) + /* use tauri_plugin_opener::OpenerExt; + + // Korrekte Android Settings Intent + let intent_uri = "android.settings.MANAGE_ALL_FILES_ACCESS_PERMISSION"; + + match app.opener().open_url(intent_uri, None::<&str>) { + Ok(_) => Ok("Settings opened - Enable 'Allow management of all files'".to_string()), + Err(_) => { + // Fallback: App-spezifische Settings + let app_settings = format!( + "android.settings.APPLICATION_DETAILS_SETTINGS?package={}", + app.config().identifier + ); + match app.opener().open_url(&app_settings, None::<&str>) { + Ok(_) => Ok("App settings opened - Go to Permissions > Files and media".to_string()), + Err(_) => Ok("Manually go to: Settings > Apps > Special app access > All files access > HaexHub > Allow".to_string()) + } + } + }*/ +} + +#[cfg(target_os = "android")] +#[tauri::command] +pub async fn has_storage_permission() -> Result { + use std::path::Path; + + // Teste Schreibzugriff auf externen Speicher + let test_paths = [ + "/storage/emulated/0/Android", + "/sdcard/Android", + "/storage/emulated/0", + ]; + + for path in &test_paths { + if Path::new(path).exists() { + // Versuche Testdatei zu erstellen + let test_file = format!("{}/haex_test.tmp", path); + match std::fs::write(&test_file, "test") { + Ok(_) => { + let _ = std::fs::remove_file(&test_file); + return Ok(true); + } + Err(_) => continue, + } + } + } + + Ok(false) +} + +#[cfg(target_os = "android")] +#[tauri::command] +pub async fn get_external_storage_paths() -> Result, String> { + let mut paths = Vec::new(); + + let common_paths = [ + "/storage/emulated/0", + "/sdcard", + "/storage/emulated/0/Download", + "/storage/emulated/0/Documents", + "/storage/emulated/0/Pictures", + "/storage/emulated/0/DCIM", + ]; + + for path in &common_paths { + if std::path::Path::new(path).exists() { + paths.push(path.to_string()); + } + } + + Ok(paths) +} + +#[cfg(not(target_os = "android"))] +#[tauri::command] +pub async fn request_storage_permission(_app: tauri::AppHandle) -> Result { + Ok("aaaaaaaa".to_string()) +} + +#[cfg(not(target_os = "android"))] +#[tauri::command] +pub async fn has_storage_permission() -> Result { + Ok(true) +} + +#[cfg(not(target_os = "android"))] +#[tauri::command] +pub async fn get_external_storage_paths() -> Result, String> { + Ok(vec![]) +} diff --git a/src-tauri/src/crdt/log.rs b/src-tauri/src/crdt/log.rs new file mode 100644 index 0000000..d3769aa --- /dev/null +++ b/src-tauri/src/crdt/log.rs @@ -0,0 +1,22 @@ +// src/entities/crdt_log.rs +use sea_orm::entity::prelude::*; + +#[sea_orm(table_name = "crdt_log")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = true)] + pub id: i64, + pub hlc_timestamp: String, + pub op_type: String, + pub table_name: String, + pub row_pk: String, // Wird als JSON-String gespeichert + #[sea_orm(nullable)] + pub column_name: Option, + #[sea_orm(nullable)] + pub value: Option, + #[sea_orm(nullable)] + pub old_value: Option, +} + +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/src-tauri/src/crdt/proxy.rs b/src-tauri/src/crdt/proxy.rs new file mode 100644 index 0000000..59f4e53 --- /dev/null +++ b/src-tauri/src/crdt/proxy.rs @@ -0,0 +1,165 @@ +// In src-tauri/src/sql_proxy.rs + +use rusqlite::Connection; +use sqlparser::ast::Statement; +use sqlparser::ast::{ColumnDef, DataType, Expr, Ident, Query, Statement, TableWithJoins, Value}; +use sqlparser::dialect::SQLiteDialect; +use sqlparser::parser::Parser; +use sqlparser::visit_mut::{self, VisitorMut}; +use std::ops::ControlFlow; + +// Der Name der Tombstone-Spalte als Konstante, um "Magic Strings" zu vermeiden. +pub const TOMBSTONE_COLUMN_NAME: &str = "tombstone"; +const EXCLUDED_TABLES: &[&str] = &["crdt_log"]; + +// Die Hauptstruktur unseres Proxys. +// Sie ist zustandslos, da wir uns gegen einen Schema-Cache entschieden haben. +pub struct SqlProxy; + +impl SqlProxy { + pub fn new() -> Self { + Self {} + } + + // Die zentrale Ausführungsfunktion + pub fn execute(&self, sql: &str, conn: &Connection) -> Result<(), String> { + // 1. Parsen des SQL-Strings in einen oder mehrere ASTs. + // Ein String kann mehrere, durch Semikolon getrennte Anweisungen enthalten. + let dialect = SQLiteDialect {}; + let mut ast_vec = + Parser::parse_sql(&dialect, sql).map_err(|e| format!("SQL-Parse-Fehler: {}", e))?; + + // 2. Wir durchlaufen und transformieren jedes einzelne Statement im AST-Vektor. + for statement in &mut ast_vec { + self.transform_statement(statement)?; + } + + // 3. Ausführen der (möglicherweise modifizierten) Anweisungen in einer einzigen Transaktion. + // Dies stellt sicher, dass alle Operationen atomar sind. + let tx = conn.transaction().map_err(|e| e.to_string())?; + for statement in ast_vec { + let final_sql = statement.to_string(); + tx.execute(&final_sql) + .map_err(|e| format!("DB-Ausführungsfehler bei '{}': {}", final_sql, e))?; + + // Wenn es ein CREATE/ALTER TABLE war, müssen die Trigger neu erstellt werden. + // Dies geschieht innerhalb derselben Transaktion. + if let Statement::CreateTable { name, .. } | Statement::AlterTable { name, .. } = + statement + { + let table_name = name.0.last().unwrap().value.clone(); + let trigger_manager = crate::trigger_manager::TriggerManager::new(&tx); + trigger_manager + .setup_triggers_for_table(&table_name) + .map_err(|e| { + format!("Trigger-Setup-Fehler für Tabelle '{}': {}", table_name, e) + })?; + } + } + tx.commit().map_err(|e| e.to_string())?; + + Ok(()) + } + + // Diese Methode wendet die Transformation auf ein einzelnes Statement an. + fn transform_statement(&self, statement: &mut Statement) -> Result<(), String> { + let mut visitor = TombstoneVisitor; + // `visit` durchläuft den AST und ruft die entsprechenden `visit_*_mut` Methoden auf. + statement.visit(&mut visitor); + Ok(()) + } +} + +struct TombstoneVisitor; + +impl TombstoneVisitor { + fn is_audited_table(&self, table_name: &str) -> bool { + !EXCLUDED_TABLES.contains(&table_name.to_lowercase().as_str()) + } +} + +impl VisitorMut for TombstoneVisitor { + type Break = (); + + // Diese Methode wird für jedes Statement im AST aufgerufen + fn visit_statement_mut(&mut self, stmt: &mut Statement) -> ControlFlow { + match stmt { + // Fall 1: CREATE TABLE + Statement::CreateTable { name, columns, .. } => { + let table_name = name.0.last().unwrap().value.as_str(); + if self.is_audited_table(table_name) { + // Füge die 'tombstone'-Spalte hinzu, wenn sie nicht existiert + if !columns + .iter() + .any(|c| c.name.value.to_lowercase() == TOMBSTONE_COLUMN_NAME) + { + columns.push(ColumnDef { + name: Ident::new(TOMBSTONE_COLUMN_NAME), + data_type: DataType::Integer, + collation: None, + options: vec![], // Default ist 0 + }); + } + } + } + + // Fall 2: DELETE + Statement::Delete(del_stmt) => { + // Wandle das DELETE-Statement in ein UPDATE-Statement um + let new_update = Statement::Update { + table: del_stmt.from.clone(), + assignments: vec![], + value: Box::new(Expr::Value(Value::Number("1".to_string(), false))), + from: None, + selection: del_stmt.selection.clone(), + returning: None, + }; + // Ersetze das aktuelle Statement im AST + *stmt = new_update; + } + _ => {} + } + + // Setze die Traversierung für untergeordnete Knoten fort (z.B. SELECTs) + visit_mut::walk_statement_mut(self, stmt) + } + + // Diese Methode wird für jede Query (auch Subqueries) aufgerufen + fn visit_query_mut(&mut self, query: &mut Query) -> ControlFlow { + // Zuerst rekursiv in die Tiefe gehen, um innere Queries zuerst zu bearbeiten + visit_mut::walk_query_mut(self, query); + + // Dann die WHERE-Klausel der aktuellen Query anpassen + if let Some(from_clause) = query.body.as_select_mut().map(|s| &mut s.from) { + // (Hier würde eine komplexere Logik zur Analyse der Joins und Tabellen stehen) + // Vereinfacht nehmen wir an, wir fügen es für die erste Tabelle hinzu. + let table_name = if let Some(relation) = from_clause.get_mut(0) { + // Diese Logik muss verfeinert werden, um Aliase etc. zu behandeln + relation.relation.to_string() + } else { + "".to_string() + }; + + if self.is_audited_table(&table_name) { + let tombstone_check = Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new(TOMBSTONE_COLUMN_NAME))), + op: sqlparser::ast::BinaryOperator::Eq, + right: Box::new(Expr::Value(Value::Number("0".to_string(), false))), + }; + + let existing_selection = query.selection.take(); + let new_selection = match existing_selection { + Some(expr) => Expr::BinaryOp { + left: Box::new(expr), + op: sqlparser::ast::BinaryOperator::And, + right: Box::new(tombstone_check), + }, + None => tombstone_check, + }; + query.selection = Some(Box::new(new_selection)); + } + } + + ControlFlow::Continue(()) + } +} diff --git a/src-tauri/src/crdt/trigger.rs b/src-tauri/src/crdt/trigger.rs new file mode 100644 index 0000000..e8c2719 --- /dev/null +++ b/src-tauri/src/crdt/trigger.rs @@ -0,0 +1,124 @@ +// In src-tauri/src/trigger_manager.rs -> impl<'a> TriggerManager<'a> + +// In einem neuen Modul, z.B. src-tauri/src/trigger_manager.rs +use crate::sql_proxy::ColumnInfo; +use rusqlite::{Result, Transaction}; + +pub struct TriggerManager<'a> { + tx: &'a Transaction<'a>, +} + +impl<'a> TriggerManager<'a> { + pub fn new(tx: &'a Transaction<'a>) -> Self { + Self { tx } + } + + // Die Hauptfunktion, die alles einrichtet + pub fn setup_triggers_for_table(&self, table_name: &str) -> Result<()> { + let columns = self.get_table_schema(table_name)?; + let pk_cols: Vec<_> = columns + .iter() + .filter(|c| c.is_pk) + .map(|c| c.name.as_str()) + .collect(); + let other_cols: Vec<_> = columns + .iter() + .filter(|c| !c.is_pk && c.name != "tombstone") + .map(|c| c.name.as_str()) + .collect(); + + let drop_sql = self.generate_drop_triggers_sql(table_name); + let insert_sql = self.generate_insert_trigger_sql(table_name, &pk_cols, &other_cols); + let update_sql = self.generate_update_trigger_sql(table_name, &pk_cols, &other_cols); + + self.tx.execute_batch(&drop_sql)?; + self.tx.execute_batch(&insert_sql)?; + self.tx.execute_batch(&update_sql)?; + + Ok(()) + } + + fn get_table_schema(&self, table_name: &str) -> Result> { + let sql = format!("PRAGMA table_info('{}')", table_name); + let mut stmt = self.tx.prepare(&sql)?; + let rows = stmt.query_map(|row| { + let pk_val: i64 = row.get(5)?; + Ok(ColumnInfo { + name: row.get(1)?, + is_pk: pk_val > 0, + }) + })?; + + let mut columns = Vec::new(); + for row_result in rows { + columns.push(row_result?); + } + Ok(columns) + } + + //... Implementierung der SQL-Generierungsfunktionen... + + fn generate_update_trigger_sql(&self, table_name: &str, pks: &[&str], cols: &[&str]) -> String { + // Erstellt dynamisch die Key-Value-Paare für das JSON-Objekt des Primärschlüssels. + let pk_json_payload_new = pks + .iter() + .map(|pk| format!("'{}', NEW.\"{}\"", pk, pk)) + .collect::>() + .join(", "); + + let pk_json_payload_old = pks + .iter() + .map(|pk| format!("'{}', OLD.\"{}\"", pk, pk)) + .collect::>() + .join(", "); + + // Erstellt die einzelnen INSERT-Anweisungen für jede Spalte + let column_updates = cols.iter().map(|col| format!( + r#" + -- Protokolliere die Spaltenänderung, wenn sie stattgefunden hat und es kein Soft-Delete ist + INSERT INTO crdt_log (hlc_timestamp, op_type, table_name, row_pk, column_name, value, old_value) + SELECT + 'placeholder_hlc', -- TODO: HLC-Funktion hier aufrufen + 'UPDATE', + '{table}', + json_object({pk_payload_new}), + '{column}', + json_object('value', NEW."{column}"), + json_object('value', OLD."{column}") + WHERE + NEW."{column}" IS NOT OLD."{column}" + "#, + table = table_name, + pk_payload_new = pk_json_payload_new, + column = col + )).collect::>().join("\n"); + + // Erstellt die Logik für den Soft-Delete + let delete_logic = format!( + r#" + -- Protokolliere den Soft-Delete + INSERT INTO crdt_log (hlc_timestamp, op_type, table_name, row_pk) + SELECT + 'placeholder_hlc', -- TODO: HLC-Funktion hier aufrufen + 'DELETE', + '{table}', + json_object({pk_payload_old}) + WHERE + OLD.{tombstone_col} = 0 + "#, + table = table_name, + pk_payload_old = pk_json_payload_old + ); + + // Kombiniert alles zu einem einzigen Trigger + format!( + "CREATE TRIGGER IF NOT EXISTS {table_name}_crdt_update + AFTER UPDATE ON {table_name} + FOR EACH ROW + BEGIN + {column_updates} + {delete_logic} + END;" + ) + } +} diff --git a/src-tauri/src/database/core.rs b/src-tauri/src/database/core.rs index 9b84402..8d6dca9 100644 --- a/src-tauri/src/database/core.rs +++ b/src-tauri/src/database/core.rs @@ -6,8 +6,8 @@ use rusqlite::{ Connection, OpenFlags, ToSql, }; use serde_json::Value as JsonValue; +use std::fs; use std::path::Path; -use std::{fs, path::PathBuf}; use tauri::State; // --- Hilfsfunktion: Konvertiert JSON Value zu etwas, das rusqlite versteht --- // Diese Funktion ist etwas knifflig wegen Ownership und Lifetimes. @@ -168,7 +168,13 @@ pub fn open_and_init_db(path: &str, key: &str, create: bool) -> Result, ) -> Result { + /* let vault_path = app_handle + .path() + .resolve(format!("vaults/{}", path), BaseDirectory::AppLocalData) + .map_err(|e| format!("Fehler {}", e))? + .into_os_string() + .into_string() + .unwrap(); */ if !std::path::Path::new(&path).exists() { - return Err("File not found ".into()); + return Err(format!("File not found {}", path).into()); } let conn = diff --git a/src-tauri/src/database/sql_proxy.rs b/src-tauri/src/database/sql_proxy.rs deleted file mode 100644 index 5f7307a..0000000 --- a/src-tauri/src/database/sql_proxy.rs +++ /dev/null @@ -1,63 +0,0 @@ -// src-tauri/src/sql_proxy.rs - -use rusqlite::Connection; -use sqlparser::ast::Statement; -use sqlparser::dialect::SQLiteDialect; -use sqlparser::parser::Parser; -use std::sync::{Arc, Mutex}; - -// Der Schema-Cache wird später benötigt, um zu wissen, welche Tabellen eine 'tombstone'-Spalte haben. -// Für den Anfang lassen wir ihn leer. -pub struct SchemaCache { - // TODO: z.B. HashMap> für Tabellen und ihre Spalten -} - -impl SchemaCache { - pub fn new() -> Self { - Self {} - } - // TODO: Methoden zum Befüllen und Abfragen des Caches -} - -// Die Hauptstruktur unseres Proxys -pub struct SqlProxy { - // Wir benötigen eine threadsichere Referenz auf den Schema-Cache - schema_cache: Arc>, -} - -impl SqlProxy { - pub fn new(schema_cache: Arc>) -> Self { - Self { schema_cache } - } - - // Die zentrale Ausführungsfunktion - pub fn execute(&self, sql: &str, conn: &Connection) -> Result<(), String> { - // 1. Parsen des SQL-Strings in einen AST - let dialect = SQLiteDialect {}; - let mut ast = - Parser::parse_sql(&dialect, sql).map_err(|e| format!("SQL-Parse-Fehler: {}", e))?; - - // Sicherstellen, dass wir nur eine Anweisung haben - if ast.len() != 1 { - return Err("Nur einzelne SQL-Anweisungen werden unterstützt.".to_string()); - } - let statement = &mut ast; - - // 2. Umschreiben des AST (Logik folgt in Abschnitt 2) - self.transform_statement(statement)?; - - // 3. Ausführen der (möglicherweise modifizierten) Anweisung - let final_sql = statement.to_string(); - conn.execute(&final_sql) - .map_err(|e| format!("DB-Ausführungsfehler: {}", e))?; - - Ok(()) - } - - // Platzhalter für die Transformationslogik - fn transform_statement(&self, statement: &mut Statement) -> Result<(), String> { - // HIER KOMMT DIE MAGIE HIN - // TODO: Implementierung der `CREATE TABLE`, `DELETE` und `SELECT` Transformationen - Ok(()) - } -} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 86e3160..5afd18c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,4 +1,5 @@ //mod browser; +mod android_storage; mod database; mod extension; mod models; @@ -44,13 +45,15 @@ pub fn run() { }) .manage(DbConnection(Arc::new(Mutex::new(None)))) .manage(ExtensionState::default()) - .plugin(tauri_plugin_notification::init()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_http::init()) + .plugin(tauri_plugin_notification::init()) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_os::init()) + .plugin(tauri_plugin_persisted_scope::init()) .plugin(tauri_plugin_store::Builder::new().build()) + .plugin(tauri_plugin_android_fs::init()) //.plugin(tauri_plugin_sql::Builder::new().build()) .invoke_handler(tauri::generate_handler![ database::create_encrypted_database, @@ -63,6 +66,9 @@ pub fn run() { extension::copy_directory, extension::database::extension_sql_execute, extension::database::extension_sql_select, + android_storage::request_storage_permission, + android_storage::has_storage_permission, + android_storage::get_external_storage_paths, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index aa12a41..e67f0c3 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -55,7 +55,7 @@ "icons/icon.icns", "icons/icon.ico" ], - "resources": ["database/vault.db", "resources/"], + "resources": ["database/vault.db"], "linux": { "appimage": { diff --git a/src/components/vault/button/create.vue b/src/components/vault/button/create.vue index 0e9dade..2a1e259 100644 --- a/src/components/vault/button/create.vue +++ b/src/components/vault/button/create.vue @@ -69,6 +69,9 @@ import { save } from '@tauri-apps/plugin-dialog' import { onKeyStroke } from '@vueuse/core' import { useVaultStore } from '~/stores/vault' import { vaultDatabaseSchema } from './schema' +import { BaseDirectory, readFile, writeFile } from '@tauri-apps/plugin-fs' +import { resolveResource } from '@tauri-apps/api/path' +//import { convertFileSrc } from "@tauri-apps/api/tauri"; onKeyStroke('Enter', (e) => { e.preventDefault() @@ -123,12 +126,22 @@ const onCreateAsync = async () => { open.value = false try { + const template_vault_path = await resolveResource('database/vault.db') + + const template_vault = await readFile(template_vault_path) + database.path = await save({ defaultPath: database.name.endsWith('.db') ? database.name : `${database.name}.db`, }) + if (!database.path) return + + await writeFile('temp_vault.db', template_vault, { + baseDir: BaseDirectory.AppLocalData, + }) + console.log('data', database) if (database.path && database.password) { @@ -146,7 +159,7 @@ const onCreateAsync = async () => { } } catch (error) { console.error(error) - add({ type: 'error', text: JSON.stringify(error) }) + add({ type: 'error', text: `${error}` }) } } diff --git a/src/components/vault/button/open.vue b/src/components/vault/button/open.vue index 20fb502..338a504 100644 --- a/src/components/vault/button/open.vue +++ b/src/components/vault/button/open.vue @@ -18,7 +18,7 @@ HaexVault - + Path1: {{ test }}
{{ props.path ?? database.path }}
@@ -41,6 +41,15 @@ diff --git a/src/pages/vault/[vaultId]/index.vue b/src/pages/vault/[vaultId]/index.vue index 375fa56..8080e78 100644 --- a/src/pages/vault/[vaultId]/index.vue +++ b/src/pages/vault/[vaultId]/index.vue @@ -5,6 +5,8 @@ v-bind="extension" :key="extension.id" /> + Storage Request + res: {{ res }} @@ -13,5 +15,17 @@ definePageMeta({ name: 'vaultOverview', }) +const storage = useAndroidStorage() const extensionStore = useExtensionsStore() + +const res = ref() + +const requesty = async () => { + try { + res.value = await storage.requestStoragePermission() + res.value += ' wat the fuk' + } catch (error) { + res.value = error + } +} diff --git a/src/stores/vault/index.ts b/src/stores/vault/index.ts index 86c5f08..4650b62 100644 --- a/src/stores/vault/index.ts +++ b/src/stores/vault/index.ts @@ -51,8 +51,8 @@ export const useVaultStore = defineStore('vaultStore', () => { if (result !== 'success') throw new Error(result) const vaultId = await getVaultIdAsync(path) - const seperator = platform() === 'windows' ? '\\' : '/' - const fileName = path.split(seperator).pop() + + const fileName = getFileName(path) openVaults.value = { ...openVaults.value, diff --git a/src/utils/androidStorage.ts b/src/utils/androidStorage.ts new file mode 100644 index 0000000..c9d16ed --- /dev/null +++ b/src/utils/androidStorage.ts @@ -0,0 +1,38 @@ +import { invoke } from '@tauri-apps/api/core' + +export const useAndroidStorage = () => { + const requestStoragePermission = async (): Promise => { + try { + return await invoke( + 'plugin:android-fs|openManageAllFilesAccessPermissionSettings', + ) + } catch (error) { + console.error('Failed to request storage permission:', error) + throw error + } + } + + const hasStoragePermission = async (): Promise => { + try { + return await invoke('has_storage_permission') + } catch (error) { + console.error('Failed to check storage permission:', error) + return false + } + } + + const getExternalStoragePaths = async (): Promise => { + try { + return await invoke('get_external_storage_paths') + } catch (error) { + console.error('Failed to get storage paths:', error) + return [] + } + } + + return { + requestStoragePermission, + hasStoragePermission, + getExternalStoragePaths, + } +} diff --git a/src/utils/helper.ts b/src/utils/helper.ts index 582ba40..9693121 100644 --- a/src/utils/helper.ts +++ b/src/utils/helper.ts @@ -1,3 +1,4 @@ +import { platform } from '@tauri-apps/plugin-os' import type { LocationQueryValue, RouteLocationRawI18n } from 'vue-router' export const bytesToBase64DataUrlAsync = async ( @@ -67,7 +68,7 @@ export const readableFileSize = (sizeInByte: number | string = 0) => { export const getSingleRouteParam = ( param: string | string[] | LocationQueryValue | LocationQueryValue[], ): string => { - const _param = Array.isArray(param) ? param.at(0) ?? '' : param ?? '' + const _param = Array.isArray(param) ? (param.at(0) ?? '') : (param ?? '') //console.log('getSingleRouteParam found:', _param, param) return decodeURIComponent(_param) } @@ -230,3 +231,8 @@ export const areObjectsEqual = (valueA: unknown, valueB: unknown): boolean => { // 5. Wenn die Schleife durchläuft, sind die Objekte gleich return true } + +export const getFileName = (fullPath: string) => { + const seperator = platform() === 'windows' ? '\\' : '/' + return fullPath.split(seperator).pop() +}