From 92892dec8d37c1db477860f069f2b3f95dd9a8a0 Mon Sep 17 00:00:00 2001 From: sharkAndshark Date: Tue, 17 Mar 2026 10:35:26 +0800 Subject: [PATCH 01/16] =?UTF-8?q?feat:=20=E4=BB=8E=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E5=AF=BC=E5=85=A5=E6=96=87=E4=BB=B6=EF=BC=88=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E6=A8=A1=E5=BC=8F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现 Issue #167 后端: - 新增 server_files_handlers.rs 处理服务器文件 API - GET /api/server-files/directories - 获取可导入目录列表 - GET /api/server-files/browse - 浏览目录内容 - POST /api/server-files/import - 导入文件(引用模式) - files 表新增 source_type 字段('upload' | 'server_import') 前端: - 新增「从服务器导入」按钮(仅管理员可见) - 新增服务器文件导入弹窗 - 支持浏览目录、选择文件、批量导入 安全: - 路径白名单:仅允许访问 SERVER_DATA_DIRS 配置的目录 - 路径遍历防护:拒绝 .. 和符号链接跳出白名单 - 文件类型验证:与上传一致 --- Cargo.lock | 480 +++++++++++++-------------- backend/src/db.rs | 4 + backend/src/lib.rs | 1 + backend/src/routes.rs | 7 +- backend/src/server_files_handlers.rs | 401 ++++++++++++++++++++++ frontend/src/App.jsx | 265 +++++++++++++++ frontend/src/api.js | 35 ++ 7 files changed, 940 insertions(+), 253 deletions(-) create mode 100644 backend/src/server_files_handlers.rs diff --git a/Cargo.lock b/Cargo.lock index 6a8599e1..515277a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 4 +version = 3 [[package]] name = "adler2" @@ -88,9 +88,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -103,15 +103,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -159,9 +159,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "arrow" -version = "56.2.0" +version = "57.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e833808ff2d94ed40d9379848a950d995043c7fb3e81a30b383f4c6033821cc" +checksum = "e4754a624e5ae42081f464514be454b39711daae0458906dacde5f4c632f33a8" dependencies = [ "arrow-arith", "arrow-array", @@ -177,23 +177,23 @@ dependencies = [ [[package]] name = "arrow-arith" -version = "56.2.0" +version = "57.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad08897b81588f60ba983e3ca39bda2b179bdd84dced378e7df81a5313802ef8" +checksum = "f7b3141e0ec5145a22d8694ea8b6d6f69305971c4fa1c1a13ef0195aef2d678b" dependencies = [ "arrow-array", "arrow-buffer", "arrow-data", "arrow-schema", "chrono", - "num", + "num-traits", ] [[package]] name = "arrow-array" -version = "56.2.0" +version = "57.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8548ca7c070d8db9ce7aa43f37393e4bfcf3f2d3681df278490772fd1673d08d" +checksum = "4c8955af33b25f3b175ee10af580577280b4bd01f7e823d94c7cdef7cf8c9aef" dependencies = [ "ahash 0.8.12", "arrow-buffer", @@ -202,29 +202,33 @@ dependencies = [ "chrono", "half", "hashbrown 0.16.1", - "num", + "num-complex", + "num-integer", + "num-traits", ] [[package]] name = "arrow-buffer" -version = "56.2.0" +version = "57.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e003216336f70446457e280807a73899dd822feaf02087d31febca1363e2fccc" +checksum = "c697ddca96183182f35b3a18e50b9110b11e916d7b7799cbfd4d34662f2c56c2" dependencies = [ "bytes", "half", - "num", + "num-bigint", + "num-traits", ] [[package]] name = "arrow-cast" -version = "56.2.0" +version = "57.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919418a0681298d3a77d1a315f625916cb5678ad0d74b9c60108eb15fd083023" +checksum = "646bbb821e86fd57189c10b4fcdaa941deaf4181924917b0daa92735baa6ada5" dependencies = [ "arrow-array", "arrow-buffer", "arrow-data", + "arrow-ord", "arrow-schema", "arrow-select", "atoi", @@ -233,27 +237,28 @@ dependencies = [ "comfy-table", "half", "lexical-core", - "num", + "num-traits", "ryu", ] [[package]] name = "arrow-data" -version = "56.2.0" +version = "57.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5c64fff1d142f833d78897a772f2e5b55b36cb3e6320376f0961ab0db7bd6d0" +checksum = "1fdd994a9d28e6365aa78e15da3f3950c0fdcea6b963a12fa1c391afb637b304" dependencies = [ "arrow-buffer", "arrow-schema", "half", - "num", + "num-integer", + "num-traits", ] [[package]] name = "arrow-ord" -version = "56.2.0" +version = "57.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c8f82583eb4f8d84d4ee55fd1cb306720cddead7596edce95b50ee418edf66f" +checksum = "f7d8f1870e03d4cbed632959498bcc84083b5a24bded52905ae1695bd29da45b" dependencies = [ "arrow-array", "arrow-buffer", @@ -264,9 +269,9 @@ dependencies = [ [[package]] name = "arrow-row" -version = "56.2.0" +version = "57.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d07ba24522229d9085031df6b94605e0f4b26e099fb7cdeec37abd941a73753" +checksum = "18228633bad92bff92a95746bbeb16e5fc318e8382b75619dec26db79e4de4c0" dependencies = [ "arrow-array", "arrow-buffer", @@ -277,32 +282,32 @@ dependencies = [ [[package]] name = "arrow-schema" -version = "56.2.0" +version = "57.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3aa9e59c611ebc291c28582077ef25c97f1975383f1479b12f3b9ffee2ffabe" +checksum = "8c872d36b7bf2a6a6a2b40de9156265f0242910791db366a2c17476ba8330d68" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "arrow-select" -version = "56.2.0" +version = "57.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c41dbbd1e97bfcaee4fcb30e29105fb2c75e4d82ae4de70b792a5d3f66b2e7a" +checksum = "68bf3e3efbd1278f770d67e5dc410257300b161b93baedb3aae836144edcaf4b" dependencies = [ "ahash 0.8.12", "arrow-array", "arrow-buffer", "arrow-data", "arrow-schema", - "num", + "num-traits", ] [[package]] name = "arrow-string" -version = "56.2.0" +version = "57.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53f5183c150fbc619eede22b861ea7c0eebed8eaac0333eaa7f6da5205fd504d" +checksum = "85e968097061b3c0e9fe3079cf2e703e487890700546b5b0647f60fca1b5a8d8" dependencies = [ "arrow-array", "arrow-buffer", @@ -310,7 +315,7 @@ dependencies = [ "arrow-schema", "arrow-select", "memchr", - "num", + "num-traits", "regex", "regex-syntax", ] @@ -323,7 +328,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -453,7 +458,7 @@ checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -516,7 +521,7 @@ checksum = "523ab528ce3a7ada6597f8ccf5bd8d85ebe26d5edf311cad4d1d3cfb2d357ac6" dependencies = [ "base64", "blowfish", - "getrandom 0.4.1", + "getrandom 0.4.2", "subtle", "zeroize", ] @@ -529,9 +534,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "bitvec" @@ -590,14 +595,14 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytecheck" @@ -650,9 +655,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.55" +version = "1.2.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", "jobserver", @@ -696,9 +701,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.60" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" dependencies = [ "clap_builder", "clap_derive", @@ -706,9 +711,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.60" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -718,21 +723,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.55" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "cocoa" @@ -766,18 +771,17 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "comfy-table" -version = "7.1.2" +version = "7.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d05af1e006a2407bedef5af410552494ce5be9090444dbbcb57258c1af3d56" +checksum = "958c5d6ecf1f214b4c2bbbbf6ab9523a864bd136dcf71a7e8904799acfe1ad47" dependencies = [ - "strum 0.26.3", - "strum_macros 0.26.4", + "unicode-segmentation", "unicode-width", ] @@ -928,15 +932,15 @@ dependencies = [ [[package]] name = "deflate64" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" +checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2" [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", "serde_core", @@ -950,7 +954,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -972,14 +976,14 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "duckdb" -version = "1.4.4" +version = "1.10500.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8685352ce688883098b61a361e86e87df66fc8c444f4a2411e884c16d5243a65" +checksum = "6a2048e4963a37ec458d9e93444192e0e949ac0a188d201ea53472f2c42db822" dependencies = [ "arrow", "cast", @@ -990,7 +994,7 @@ dependencies = [ "libduckdb-sys", "num-integer", "rust_decimal", - "strum 0.27.2", + "strum", ] [[package]] @@ -1001,9 +1005,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "embed-resource" -version = "3.0.6" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a075fc573c64510038d7ee9abc7990635863992f83ebc52c8b433b8411a02e" +checksum = "47ec73ddcf6b7f23173d5c3c5a32b5507dc0a734de7730aa14abc5d5e296bb5f" dependencies = [ "cc", "memchr", @@ -1120,7 +1124,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1146,9 +1150,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1160,9 +1164,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -1170,44 +1174,44 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-io", @@ -1216,7 +1220,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -1261,21 +1264,21 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 6.0.0", "wasip2", "wasip3", "wasm-bindgen", @@ -1656,9 +1659,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.11.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" @@ -1703,9 +1706,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -1788,15 +1791,15 @@ checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" [[package]] name = "libc" -version = "0.2.182" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libduckdb-sys" -version = "1.4.4" +version = "1.10500.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78bacb8933586cee3b550c39b610d314f9b7a48701ac7a914a046165a4ad8da" +checksum = "6df9db064ff17120305ec6b7aee048f766cdf2a3ce9ffbb6953aaa3634605030" dependencies = [ "cc", "flate2", @@ -1817,13 +1820,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "libc", - "redox_syscall 0.7.0", + "plain", + "redox_syscall 0.7.3", ] [[package]] @@ -2011,20 +2015,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - [[package]] name = "num-bigint" version = "0.4.6" @@ -2059,28 +2049,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -2117,7 +2085,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -2140,9 +2108,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "once_cell_polyfill" @@ -2222,9 +2190,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pin-utils" @@ -2238,6 +2206,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "polyval" version = "0.6.2" @@ -2322,14 +2296,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ "toml_edit", ] @@ -2363,7 +2337,7 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2408,9 +2382,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.13" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ "bytes", "getrandom 0.3.4", @@ -2443,9 +2417,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -2456,6 +2430,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "radium" version = "0.7.0" @@ -2527,16 +2507,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "redox_syscall" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -2564,9 +2544,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rend" @@ -2676,7 +2656,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "fallible-iterator 0.3.0", "fallible-streaming-iterator", "hashlink 0.11.0", @@ -2722,7 +2702,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys", @@ -2731,9 +2711,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "once_cell", "ring", @@ -2772,9 +2752,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "scopeguard" @@ -2821,7 +2801,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2961,12 +2941,12 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3010,32 +2990,13 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" - [[package]] name = "strum" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros 0.27.2", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.114", + "strum_macros", ] [[package]] @@ -3047,7 +3008,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3069,9 +3030,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -3095,7 +3056,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3123,12 +3084,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.26.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.1", + "getrandom 0.4.2", "once_cell", "rustix", "windows-sys 0.61.2", @@ -3151,7 +3112,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3216,9 +3177,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ "tinyvec_macros", ] @@ -3248,13 +3209,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3315,7 +3276,7 @@ dependencies = [ "indexmap", "serde_core", "serde_spanned", - "toml_datetime", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", "winnow", @@ -3330,14 +3291,23 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.0.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.25.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" dependencies = [ "indexmap", - "toml_datetime", + "toml_datetime 1.0.0+spec-1.1.0", "toml_parser", "winnow", ] @@ -3395,7 +3365,7 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "futures-core", "futures-util", @@ -3501,7 +3471,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3527,9 +3497,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", @@ -3591,9 +3561,9 @@ checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-normalization" @@ -3610,6 +3580,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.2.2" @@ -3674,7 +3650,7 @@ version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" dependencies = [ - "getrandom 0.4.1", + "getrandom 0.4.2", "js-sys", "serde_core", "wasm-bindgen", @@ -3771,9 +3747,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", @@ -3784,9 +3760,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" dependencies = [ "cfg-if", "futures-util", @@ -3798,9 +3774,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3808,22 +3784,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] @@ -3856,7 +3832,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "hashbrown 0.15.5", "indexmap", "semver", @@ -3864,9 +3840,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ "js-sys", "wasm-bindgen", @@ -3925,7 +3901,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3936,7 +3912,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4130,9 +4106,9 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" dependencies = [ "memchr", ] @@ -4177,7 +4153,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn 2.0.114", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -4193,7 +4169,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -4205,7 +4181,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.10.0", + "bitflags 2.11.0", "indexmap", "log", "serde", @@ -4279,28 +4255,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.39" +version = "0.8.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.39" +version = "0.8.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4320,7 +4296,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] @@ -4341,7 +4317,7 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4374,7 +4350,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4403,7 +4379,7 @@ dependencies = [ "crc32fast", "deflate64", "flate2", - "getrandom 0.4.1", + "getrandom 0.4.2", "hmac", "indexmap", "lzma-rust2", @@ -4420,15 +4396,15 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.6.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7948af682ccbc3342b6e9420e8c51c1fe5d7bf7756002b4a3c6cabfe96a7e3c" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" [[package]] name = "zmij" -version = "1.0.19" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zopfli" diff --git a/backend/src/db.rs b/backend/src/db.rs index f8b8e2f0..f799357a 100644 --- a/backend/src/db.rs +++ b/backend/src/db.rs @@ -394,6 +394,10 @@ pub fn init_database(db_path: &Path) -> duckdb::Connection { "ALTER TABLE published_files ADD COLUMN use_aliases BOOLEAN DEFAULT TRUE", [], ); + let _ = conn.execute( + "ALTER TABLE files ADD COLUMN source_type VARCHAR DEFAULT 'upload'", + [], + ); conn.execute_batch( r" diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 068f8009..674cedcb 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -15,6 +15,7 @@ mod password; mod postgis; mod public; mod routes; +mod server_files_handlers; mod session_store; mod static_assets; mod test_routes; diff --git a/backend/src/routes.rs b/backend/src/routes.rs index 7aab1e69..85ec564b 100644 --- a/backend/src/routes.rs +++ b/backend/src/routes.rs @@ -20,6 +20,7 @@ use crate::{ }, postgis::{register_postgis_source, test_postgis_connection}, public::{get_public_pmtiles, get_public_tile, get_public_tile_meta, head_public_pmtiles}, + server_files_handlers::{browse_directory, import_files, list_directories}, upload::upload_file, workspace_handlers::{ create_workspace, delete_workspace, get_current_workspace, get_workspace, invite_member, @@ -137,7 +138,11 @@ fn build_api_router_with_auth(state: AppState, with_auth: bool) -> Router { .route( "/api/workspaces/{id}/members/{user_id}", delete(remove_member), - ); + ) + // Server file import routes + .route("/api/server-files/directories", get(list_directories)) + .route("/api/server-files/browse", get(browse_directory)) + .route("/api/server-files/import", post(import_files)); if with_auth { api_router = api_router.route_layer(axum_login::login_required!(crate::AuthBackend)); diff --git a/backend/src/server_files_handlers.rs b/backend/src/server_files_handlers.rs new file mode 100644 index 00000000..e0a538ce --- /dev/null +++ b/backend/src/server_files_handlers.rs @@ -0,0 +1,401 @@ +use axum::{ + extract::{Query, State}, + http::StatusCode, + response::{IntoResponse, Json, Response}, +}; +use axum_login::AuthSession; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; +use tracing::{info, warn}; + +use crate::{ + handlers::get_workspace_id, + http_errors::internal_error, + models::ErrorResponse, + AppState, +}; + +type ApiResult = Result; + +fn err(status: StatusCode, message: &str) -> Response { + ( + status, + Json(ErrorResponse { + error: message.to_string(), + }), + ) + .into_response() +} + +fn bad_req(message: &str) -> Response { + err(StatusCode::BAD_REQUEST, message) +} + +fn forbidden(message: &str) -> Response { + err(StatusCode::FORBIDDEN, message) +} + +fn not_found(message: &str) -> Response { + err(StatusCode::NOT_FOUND, message) +} + +fn internal_err(e: E) -> Response { + tracing::error!(error = ?e, "Internal server error"); + err(StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error") +} + +/// Get allowed data directories from environment variable +fn get_allowed_directories() -> Vec { + std::env::var("SERVER_DATA_DIRS") + .unwrap_or_else(|_| "./data".to_string()) + .split(',') + .map(|s| PathBuf::from(s.trim())) + .filter(|p| p.exists() && p.is_dir()) + .collect() +} + +/// Check if a path is within allowed directories +fn is_path_allowed(path: &PathBuf) -> bool { + let canonical = match path.canonicalize() { + Ok(c) => c, + Err(_) => return false, + }; + + let allowed_dirs = get_allowed_directories(); + for allowed in allowed_dirs { + if let Ok(allowed_canonical) = allowed.canonicalize() { + if canonical.starts_with(&allowed_canonical) { + return true; + } + } + } + false +} + +/// Validate file extension is supported +fn is_supported_extension(ext: &str) -> bool { + let ext_lower = ext.to_lowercase(); + matches!( + ext_lower.as_str(), + "zip" + | "geojson" + | "json" + | "geojsonl" + | "geojsons" + | "kml" + | "gpx" + | "topojson" + | "mbtiles" + | "pmtiles" + ) +} + +#[derive(Debug, Serialize)] +pub struct DirectoryInfo { + pub path: String, + pub name: String, +} + +#[derive(Debug, Serialize)] +pub struct DirectoriesResponse { + pub directories: Vec, +} + +/// GET /api/server-files/directories +/// Returns list of allowed directories for server file import +pub async fn list_directories( + _auth_session: AuthSession, +) -> ApiResult { + let dirs = get_allowed_directories(); + + let directories: Vec = dirs + .into_iter() + .filter_map(|p| { + let name = p.file_name()?.to_string_lossy().to_string(); + let path = p.to_string_lossy().to_string(); + Some(DirectoryInfo { path, name }) + }) + .collect(); + + Ok(Json(DirectoriesResponse { directories })) +} + +#[derive(Debug, Serialize)] +pub struct BrowseItem { + pub name: String, + #[serde(rename = "type")] + pub item_type: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub size: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub ext: Option, +} + +#[derive(Debug, Serialize)] +pub struct BrowseResponse { + #[serde(rename = "currentPath")] + pub current_path: String, + #[serde(rename = "parentPath", skip_serializing_if = "Option::is_none")] + pub parent_path: Option, + pub items: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct BrowseQuery { + pub path: Option, +} + +/// GET /api/server-files/browse?path=... +/// Browse directory contents +pub async fn browse_directory( + _auth_session: AuthSession, + Query(query): Query, +) -> ApiResult { + let base_dirs = get_allowed_directories(); + if base_dirs.is_empty() { + return Err(bad_req("No data directories configured")); + } + + // Default to first allowed directory + let target_path = match query.path { + Some(ref p) if !p.is_empty() => { + let path = PathBuf::from(p); + if !is_path_allowed(&path) { + return Err(forbidden("Access denied: path outside allowed directories")); + } + path + } + _ => base_dirs.into_iter().next().unwrap(), + }; + + // Security check for path traversal + let canonical = target_path.canonicalize().map_err(|_| { + not_found("Directory not found") + })?; + + if !canonical.is_dir() { + return Err(bad_req("Not a directory")); + } + + let read_dir = std::fs::read_dir(&canonical).map_err(internal_err)?; + + let mut items: Vec = Vec::new(); + for entry in read_dir { + let entry = match entry { + Ok(e) => e, + Err(_) => continue, + }; + + let path = entry.path(); + let name = match path.file_name() { + Some(n) => n.to_string_lossy().to_string(), + None => continue, + }; + + // Skip hidden files + if name.starts_with('.') { + continue; + } + + let metadata = match entry.metadata() { + Ok(m) => m, + Err(_) => continue, + }; + + if metadata.is_dir() { + items.push(BrowseItem { + name, + item_type: "directory".to_string(), + size: None, + ext: None, + }); + } else if metadata.is_file() { + let ext = path + .extension() + .map(|e| format!(".{}", e.to_string_lossy())); + + // Only show supported file types + if let Some(ref e) = ext { + let ext_without_dot = e.trim_start_matches('.'); + if !is_supported_extension(ext_without_dot) { + continue; + } + } else { + continue; // Skip files without extension + } + + items.push(BrowseItem { + name, + item_type: "file".to_string(), + size: Some(metadata.len()), + ext, + }); + } + } + + // Sort: directories first, then files, both alphabetically + items.sort_by(|a, b| { + match (a.item_type.as_str(), b.item_type.as_str()) { + ("directory", "file") => std::cmp::Ordering::Less, + ("file", "directory") => std::cmp::Ordering::Greater, + _ => a.name.to_lowercase().cmp(&b.name.to_lowercase()), + } + }); + + // Calculate parent path if within allowed directories + let parent_path = canonical.parent().and_then(|p| { + if is_path_allowed(&p.to_path_buf()) { + Some(p.to_string_lossy().to_string()) + } else { + None + } + }); + + Ok(Json(BrowseResponse { + current_path: canonical.to_string_lossy().to_string(), + parent_path, + items, + })) +} + +#[derive(Debug, Deserialize)] +pub struct ImportFile { + pub path: String, +} + +#[derive(Debug, Deserialize)] +pub struct ImportRequest { + pub files: Vec, +} + +#[derive(Debug, Serialize)] +pub struct ImportedFile { + pub id: String, + pub name: String, + pub path: String, + pub status: String, +} + +#[derive(Debug, Serialize)] +pub struct ImportResponse { + pub imported: Vec, +} + +/// POST /api/server-files/import +/// Import files from server (reference mode - no copy) +pub async fn import_files( + auth_session: AuthSession, + State(state): State, + Json(req): Json, +) -> ApiResult { + let workspace_id = get_workspace_id(&auth_session, &state).await?; + + if req.files.is_empty() { + return Err(bad_req("No files specified")); + } + + if req.files.len() > 50 { + return Err(bad_req("Maximum 50 files per import")); + } + + let mut imported = Vec::new(); + let conn = state.db.lock().await; + + for file in &req.files { + let path = PathBuf::from(&file.path); + + // Security checks + if !is_path_allowed(&path) { + warn!(path = %file.path, "Import blocked: path outside allowed directories"); + continue; + } + + // Check file exists and is readable + let canonical = match path.canonicalize() { + Ok(c) => c, + Err(e) => { + warn!(path = %file.path, error = %e, "Import failed: file not found"); + continue; + } + }; + + if !canonical.is_file() { + warn!(path = %file.path, "Import failed: not a file"); + continue; + } + + // Validate extension + let ext = canonical + .extension() + .map(|e| e.to_string_lossy().to_lowercase()); + + if let Some(ref e) = ext { + if !is_supported_extension(e) { + warn!(path = %file.path, ext = %e, "Import failed: unsupported file type"); + continue; + } + } else { + continue; + } + + // Get file metadata + let metadata = match std::fs::metadata(&canonical) { + Ok(m) => m, + Err(e) => { + warn!(path = %file.path, error = %e, "Import failed: cannot read metadata"); + continue; + } + }; + + let file_name = canonical + .file_stem() + .map(|n| n.to_string_lossy().to_string()) + .unwrap_or_else(|| "imported".to_string()); + + let file_size = metadata.len() as i64; + let file_type = ext.unwrap_or_else(|| "unknown".to_string()); + let file_path_str = canonical.to_string_lossy().to_string(); + let file_id = uuid::Uuid::new_v4().to_string(); + let now = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S").to_string(); + + // Insert into database with source_type = 'server_import' + let insert_result = conn.execute( + r#" + INSERT INTO files (id, name, type, size, uploaded_at, status, path, workspace_id, source_type) + VALUES (?, ?, ?, ?, ?, 'uploaded', ?, ?, 'server_import') + "#, + duckdb::params![ + &file_id, + &file_name, + &file_type, + file_size, + &now, + &file_path_str, + &workspace_id + ], + ); + + match insert_result { + Ok(_) => { + info!(file_id = %file_id, path = %file_path_str, workspace_id = %workspace_id, "Server file imported"); + imported.push(ImportedFile { + id: file_id, + name: file_name, + path: file_path_str, + status: "uploaded".to_string(), + }); + } + Err(e) => { + warn!(path = %file.path, error = %e, "Import failed: database error"); + } + } + } + + drop(conn); + + if imported.is_empty() { + return Err(bad_req("No files could be imported")); + } + + Ok(Json(ImportResponse { imported })) +} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 231b4d9c..e246bf71 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -15,6 +15,9 @@ import { updatePublishSettings, listWorkspaces, switchWorkspace, + listServerDirectories, + browseServerDirectory, + importServerFiles, } from './api.js'; import { formatSize, parseType, validateSlug } from './utils.js'; @@ -1312,6 +1315,17 @@ export default function App() { ); const [showWorkspaceDropdown, setShowWorkspaceDropdown] = useState(false); const [workspaceLoading, setWorkspaceLoading] = useState(false); + + // Server file import states + const [showServerImportModal, setShowServerImportModal] = useState(false); + const [serverDirectories, setServerDirectories] = useState([]); + const [currentBrowsePath, setCurrentBrowsePath] = useState(''); + const [parentPath, setParentPath] = useState(null); + const [browseItems, setBrowseItems] = useState([]); + const [selectedFiles, setSelectedFiles] = useState([]); + const [isLoadingBrowse, setIsLoadingBrowse] = useState(false); + const [isImporting, setIsImporting] = useState(false); + const [importError, setImportError] = useState(''); async function refreshFiles(nextSelectedId = null) { const data = await listFiles(); @@ -1411,6 +1425,87 @@ export default function App() { } } + // Server file import functions + async function openServerImportModal() { + setShowServerImportModal(true); + setImportError(''); + setSelectedFiles([]); + setIsLoadingBrowse(true); + + try { + const dirs = await listServerDirectories(); + setServerDirectories(dirs.directories || []); + + if (dirs.directories && dirs.directories.length > 0) { + await browseServerPath(dirs.directories[0].path); + } + } catch (error) { + setImportError(error instanceof Error ? error.message : '获取服务器目录失败'); + } finally { + setIsLoadingBrowse(false); + } + } + + async function browseServerPath(path) { + setIsLoadingBrowse(true); + setCurrentBrowsePath(path); + setImportError(''); + + try { + const result = await browseServerDirectory(path); + setCurrentBrowsePath(result.currentPath); + setParentPath(result.parentPath); + setBrowseItems(result.items || []); + } catch (error) { + setImportError(error instanceof Error ? error.message : '浏览目录失败'); + setBrowseItems([]); + } finally { + setIsLoadingBrowse(false); + } + } + + function toggleFileSelection(item) { + if (item.type !== 'file') return; + + const filePath = `${currentBrowsePath}/${item.name}`; + setSelectedFiles(prev => { + if (prev.includes(filePath)) { + return prev.filter(f => f !== filePath); + } else { + return [...prev, filePath]; + } + }); + } + + function formatFileSize(bytes) { + if (bytes === 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; + } + + async function handleImportFiles() { + if (selectedFiles.length === 0) { + setImportError('请选择要导入的文件'); + return; + } + + setIsImporting(true); + setImportError(''); + + try { + const result = await importServerFiles(selectedFiles); + setShowServerImportModal(false); + setSelectedFiles([]); + await refreshFiles(result.imported?.[0]?.id); + } catch (error) { + setImportError(error instanceof Error ? error.message : '导入文件失败'); + } finally { + setIsImporting(false); + } + } + async function handlePublish(fileId, options) { const result = await publishFile(fileId, options); setFiles((prev) => @@ -1702,6 +1797,15 @@ export default function App() { /> 上传 + {user?.role === 'admin' && ( + + )} {user && ( + +
+
+ ⚠️ 文件将被引用,不会被复制。原文件变更会影响数据。 +
+ + {importError && ( +
+ {importError} +
+ )} + + {/* Directory breadcrumb */} +
+ 📂 + + {currentBrowsePath || '/data'} + +
+ + {/* Directory selector */} + {serverDirectories.length > 1 && ( +
+ +
+ )} + + {isLoadingBrowse ? ( +
加载中...
+ ) : ( +
+ {/* Parent directory link */} + {parentPath && ( +
browseServerPath(parentPath)} + style={{ + padding: '10px 12px', + borderBottom: '1px solid #e0e0e0', + cursor: 'pointer', + display: 'flex', + alignItems: 'center', + gap: '8px' + }} + > + 📁 + .. +
+ )} + + {/* Items list */} + {browseItems.length === 0 ? ( +
目录为空
+ ) : ( + browseItems.map((item, index) => ( +
item.type === 'directory' ? browseServerPath(`${currentBrowsePath}/${item.name}`) : toggleFileSelection(item)} + style={{ + padding: '10px 12px', + borderBottom: index < browseItems.length - 1 ? '1px solid #e0e0e0' : 'none', + cursor: 'pointer', + display: 'flex', + alignItems: 'center', + gap: '8px', + background: item.type === 'file' && selectedFiles.includes(`${currentBrowsePath}/${item.name}`) + ? '#e3f2fd' + : 'transparent' + }} + > + {item.type === 'directory' ? ( + <> + 📁 + {item.name} + + + ) : ( + <> + {}} + onClick={(e) => e.stopPropagation()} + style={{ marginRight: '4px' }} + /> + 📄 + {item.name} + + {formatFileSize(item.size || 0)} + + + )} +
+ )) + )} +
+ )} + + {/* Selection summary */} + {selectedFiles.length > 0 && ( +
+ 已选择 {selectedFiles.length} 个文件 +
+ )} +
+
+ + +
+ + + )} ); } diff --git a/frontend/src/api.js b/frontend/src/api.js index fd691fd7..bf36ce85 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -298,3 +298,38 @@ export async function leaveWorkspace(workspaceId) { } return null; } + +// Server file import APIs +export async function listServerDirectories() { + const res = await fetchWithAuth('/api/server-files/directories'); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data.error || '获取服务器目录失败'); + } + return res.json(); +} + +export async function browseServerDirectory(path) { + const url = path + ? `/api/server-files/browse?path=${encodeURIComponent(path)}` + : '/api/server-files/browse'; + const res = await fetchWithAuth(url); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data.error || '浏览目录失败'); + } + return res.json(); +} + +export async function importServerFiles(files) { + const res = await fetchWithAuth('/api/server-files/import', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ files: files.map(f => ({ path: f })) }), + }); + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data.error || '导入文件失败'); + } + return res.json(); +} From 6c3a066dacc3bfdf29f98c878a6bcc0921fdb02c Mon Sep 17 00:00:00 2001 From: sharkAndshark Date: Tue, 17 Mar 2026 11:28:10 +0800 Subject: [PATCH 02/16] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E9=94=99=E8=AF=AF=20-=20get=5Fworkspace=5Fid=20?= =?UTF-8?q?=E5=8F=AF=E8=A7=81=E6=80=A7=E5=92=8C=E7=B1=BB=E5=9E=8B=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/handlers.rs | 2 +- backend/src/server_files_handlers.rs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/handlers.rs b/backend/src/handlers.rs index 4d0cd67d..657a1be6 100644 --- a/backend/src/handlers.rs +++ b/backend/src/handlers.rs @@ -56,7 +56,7 @@ pub type TileFileMetadata = ( Option, ); -async fn get_workspace_id( +pub async fn get_workspace_id( auth_session: &AuthSession, state: &AppState, ) -> Result)> { diff --git a/backend/src/server_files_handlers.rs b/backend/src/server_files_handlers.rs index e0a538ce..867c9bca 100644 --- a/backend/src/server_files_handlers.rs +++ b/backend/src/server_files_handlers.rs @@ -10,7 +10,6 @@ use tracing::{info, warn}; use crate::{ handlers::get_workspace_id, - http_errors::internal_error, models::ErrorResponse, AppState, }; @@ -288,7 +287,9 @@ pub async fn import_files( State(state): State, Json(req): Json, ) -> ApiResult { - let workspace_id = get_workspace_id(&auth_session, &state).await?; + let workspace_id = get_workspace_id(&auth_session, &state) + .await + .map_err(|(status, json)| err(status, &json.error))?; if req.files.is_empty() { return Err(bad_req("No files specified")); From 8bcc9d10f0e318221e99dfc4073fe9126e7b1647 Mon Sep 17 00:00:00 2001 From: sharkAndshark Date: Tue, 17 Mar 2026 11:32:50 +0800 Subject: [PATCH 03/16] =?UTF-8?q?fix:=20=E6=9C=8D=E5=8A=A1=E5=99=A8?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E6=96=87=E4=BB=B6=E5=90=8E=E8=A7=A6=E5=8F=91?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E5=A4=84=E7=90=86=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:导入的文件只插入数据库,没有触发 import_spatial_data() 等处理 修复:在导入后启动 tokio::spawn 后台任务,与上传流程一致 - 复用 upload.rs 的处理逻辑 - 支持 mbtiles/pmtiles/空间数据文件 - 导入后状态:uploaded → processing → ready/failed --- backend/src/server_files_handlers.rs | 63 ++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) diff --git a/backend/src/server_files_handlers.rs b/backend/src/server_files_handlers.rs index 867c9bca..e136396a 100644 --- a/backend/src/server_files_handlers.rs +++ b/backend/src/server_files_handlers.rs @@ -6,10 +6,12 @@ use axum::{ use axum_login::AuthSession; use serde::{Deserialize, Serialize}; use std::path::PathBuf; -use tracing::{info, warn}; +use tracing::{info, info_span, warn, Instrument}; use crate::{ handlers::get_workspace_id, + import::import_spatial_data, + mbtiles, models::ErrorResponse, AppState, }; @@ -380,11 +382,64 @@ pub async fn import_files( Ok(_) => { info!(file_id = %file_id, path = %file_path_str, workspace_id = %workspace_id, "Server file imported"); imported.push(ImportedFile { - id: file_id, - name: file_name, - path: file_path_str, + id: file_id.clone(), + name: file_name.clone(), + path: file_path_str.clone(), status: "uploaded".to_string(), }); + + // Start background processing (same as upload) + let db = state.db.clone(); + let file_id_clone = file_id.clone(); + let file_type_clone = file_type.clone(); + let file_path_clone = canonical.clone(); + let span = info_span!("server_import", file_id = %file_id, file_type = %file_type); + + tokio::spawn( + async move { + tracing::info!("Starting server file import processing"); + { + let conn = db.lock().await; + let _ = conn.execute( + "UPDATE files SET status = 'processing' WHERE id = ?", + duckdb::params![&file_id_clone], + ); + } + + let result = match file_type_clone.as_str() { + "mbtiles" => mbtiles::import_mbtiles(&db, &file_id_clone, &file_path_clone).await, + "pmtiles" => { + let conn = db.lock().await; + let _ = conn.execute( + "UPDATE files SET status = 'ready', tile_source = 'pmtiles' WHERE id = ?", + duckdb::params![&file_id_clone], + ); + Ok(()) + } + _ => import_spatial_data(&db, &file_id_clone, &file_path_clone).await, + }; + + match result { + Ok(_) => { + tracing::info!("Server file import completed successfully"); + let conn = db.lock().await; + let _ = conn.execute( + "UPDATE files SET status = 'ready' WHERE id = ?", + duckdb::params![&file_id_clone], + ); + } + Err(e) => { + tracing::error!(error = %e, "Server file import failed"); + let conn = db.lock().await; + let _ = conn.execute( + "UPDATE files SET status = 'failed', error = ? WHERE id = ?", + duckdb::params![e.to_string(), &file_id_clone], + ); + } + } + } + .instrument(span), + ); } Err(e) => { warn!(path = %file.path, error = %e, "Import failed: database error"); From 137276b82788df62d154d9b7c976ca2964214916 Mon Sep 17 00:00:00 2001 From: sharkAndshark Date: Tue, 17 Mar 2026 11:37:13 +0800 Subject: [PATCH 04/16] =?UTF-8?q?feat:=20=E6=89=80=E6=9C=89=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=8F=AF=E7=94=A8=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除管理员限制,普通成员也可使用 --- frontend/src/App.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index e246bf71..4e467068 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1797,7 +1797,7 @@ export default function App() { /> 上传 - {user?.role === 'admin' && ( + {user && ( )} @@ -2068,7 +2063,11 @@ export default function App() { {/* Server File Import Modal */} {showServerImportModal && (
setShowServerImportModal(false)}> -
e.stopPropagation()}> +
e.stopPropagation()} + >

从服务器导入

- + {importError && (
{importError} @@ -2091,23 +2090,27 @@ export default function App() { )} {/* Directory breadcrumb */} -
+
📂 - + {currentBrowsePath || '/data'}
@@ -2121,8 +2124,10 @@ export default function App() { onChange={(e) => browseServerPath(e.target.value)} style={{ fontSize: '14px' }} > - {serverDirectories.map(dir => ( - + {serverDirectories.map((dir) => ( + ))}
@@ -2142,32 +2147,41 @@ export default function App() { cursor: 'pointer', display: 'flex', alignItems: 'center', - gap: '8px' + gap: '8px', }} > 📁 ..
)} - + {/* Items list */} {browseItems.length === 0 ? ( -
目录为空
+
+ 目录为空 +
) : ( browseItems.map((item, index) => (
item.type === 'directory' ? browseServerPath(joinPath(currentBrowsePath, item.name)) : toggleFileSelection(item)} + onClick={() => + item.type === 'directory' + ? browseServerPath(joinPath(currentBrowsePath, item.name)) + : toggleFileSelection(item) + } style={{ padding: '10px 12px', - borderBottom: index < browseItems.length - 1 ? '1px solid #e0e0e0' : 'none', + borderBottom: + index < browseItems.length - 1 ? '1px solid #e0e0e0' : 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '8px', - background: item.type === 'file' && selectedFiles.includes(joinPath(currentBrowsePath, item.name)) - ? '#e3f2fd' - : 'transparent' + background: + item.type === 'file' && + selectedFiles.includes(joinPath(currentBrowsePath, item.name)) + ? '#e3f2fd' + : 'transparent', }} > {item.type === 'directory' ? ( @@ -2180,7 +2194,9 @@ export default function App() { <> {}} onClick={(e) => e.stopPropagation()} style={{ marginRight: '4px' }} @@ -2219,7 +2235,9 @@ export default function App() { onClick={handleImportFiles} disabled={isImporting || selectedFiles.length === 0} > - {isImporting ? '导入中...' : `导入选中文件${selectedFiles.length > 0 ? ` (${selectedFiles.length})` : ''}`} + {isImporting + ? '导入中...' + : `导入选中文件${selectedFiles.length > 0 ? ` (${selectedFiles.length})` : ''}`}
From 49fd7be58f5a1c8c3c84d18d1b0140d3b78887d6 Mon Sep 17 00:00:00 2001 From: sharkAndshark Date: Wed, 18 Mar 2026 01:11:43 +0800 Subject: [PATCH 14/16] =?UTF-8?q?fix:=20=E7=BB=9F=E4=B8=80=20match=20arms?= =?UTF-8?q?=20=E8=BF=94=E5=9B=9E=E7=B1=BB=E5=9E=8B=E4=B8=BA=20Result<(),?= =?UTF-8?q?=20String>?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复编译错误: - E0308: match arms 类型不匹配 - E0282: 类型推断失败 将 pmtiles 分支的返回类型从 Box 改为 String,与 import_mbtiles 和 import_spatial_data 保持一致 --- backend/src/server_files_handlers.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/backend/src/server_files_handlers.rs b/backend/src/server_files_handlers.rs index 7e182d88..69aaf7c6 100644 --- a/backend/src/server_files_handlers.rs +++ b/backend/src/server_files_handlers.rs @@ -491,20 +491,15 @@ pub async fn import_files( } } - let result = match file_type.as_str() { + let result: Result<(), String> = match file_type.as_str() { "mbtiles" => mbtiles::import_mbtiles(&db, &file_id, &file_path).await, "pmtiles" => { let conn = db.lock().await; - match conn.execute( + conn.execute( "UPDATE files SET status = 'ready', tile_source = 'pmtiles' WHERE id = ?", duckdb::params![&file_id], - ) { - Ok(_) => Ok(()), - Err(e) => { - tracing::error!(error = %e, file_id = %file_id, "Failed to update pmtiles status"); - Err(Box::new(std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) as Box) - } - } + ).map_err(|e| e.to_string())?; + Ok(()) } _ => import_spatial_data(&db, &file_id, &file_path).await, }; From 6b8a002977fdd70baea167f0f815e20bc084f8e6 Mon Sep 17 00:00:00 2001 From: sharkAndshark Date: Wed, 18 Mar 2026 01:58:08 +0800 Subject: [PATCH 15/16] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20api.js=20?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除多余空格 (line 313) - 使用箭头函数括号 (line 328) --- frontend/src/api.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/api.js b/frontend/src/api.js index bf36ce85..256a5a7c 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -310,7 +310,7 @@ export async function listServerDirectories() { } export async function browseServerDirectory(path) { - const url = path + const url = path ? `/api/server-files/browse?path=${encodeURIComponent(path)}` : '/api/server-files/browse'; const res = await fetchWithAuth(url); @@ -325,7 +325,7 @@ export async function importServerFiles(files) { const res = await fetchWithAuth('/api/server-files/import', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ files: files.map(f => ({ path: f })) }), + body: JSON.stringify({ files: files.map((f) => ({ path: f })) }), }); if (!res.ok) { const data = await res.json().catch(() => ({})); From 7c2c9a887e6f2dfdb4430f79d6e960cbc41d1746 Mon Sep 17 00:00:00 2001 From: sharkAndshark Date: Wed, 18 Mar 2026 02:28:27 +0800 Subject: [PATCH 16/16] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20async=20?= =?UTF-8?q?=E5=9D=97=E4=B8=AD=E4=B8=8D=E8=83=BD=E4=BD=BF=E7=94=A8=20=3F=20?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E7=AC=A6=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 .map_err(|e| e.to_string())? 改为 match 表达式 - async 块返回 () 不能使用 ? 操作符 --- backend/src/server_files_handlers.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/backend/src/server_files_handlers.rs b/backend/src/server_files_handlers.rs index 69aaf7c6..b5e93818 100644 --- a/backend/src/server_files_handlers.rs +++ b/backend/src/server_files_handlers.rs @@ -495,11 +495,13 @@ pub async fn import_files( "mbtiles" => mbtiles::import_mbtiles(&db, &file_id, &file_path).await, "pmtiles" => { let conn = db.lock().await; - conn.execute( + match conn.execute( "UPDATE files SET status = 'ready', tile_source = 'pmtiles' WHERE id = ?", duckdb::params![&file_id], - ).map_err(|e| e.to_string())?; - Ok(()) + ) { + Ok(_) => Ok(()), + Err(e) => Err(e.to_string()), + } } _ => import_spatial_data(&db, &file_id, &file_path).await, };