diff --git a/Cargo.lock b/Cargo.lock index 0dd61776..6372d8d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,9 +107,9 @@ dependencies = [ [[package]] name = "atom_syndication" -version = "0.12.5" +version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee79fb83c725eae67b55218870813d2fc39fd85e4f1583848ef9f4f823cfe7c" +checksum = "ec03a6e158ee0f38bfba811976ae909bc2505a4a2f4049c7e8df47df3497b119" dependencies = [ "chrono", "diligent-date-parser", @@ -130,13 +130,13 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.9" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" dependencies = [ - "async-trait", "axum-core", "bytes", + "form_urlencoded", "futures-util", "http", "http-body", @@ -152,8 +152,9 @@ dependencies = [ "pin-project-lite", "rustversion", "serde", + "serde_path_to_error", "serde_urlencoded", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tower", "tower-layer", @@ -163,11 +164,10 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.5" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" dependencies = [ - "async-trait", "bytes", "futures-util", "http", @@ -176,7 +176,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper 1.0.2", + "sync_wrapper", "tower-layer", "tower-service", "tracing", @@ -184,21 +184,19 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.6" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04" +checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b" dependencies = [ "axum", "axum-core", "bytes", - "fastrand", "futures-util", "headers", "http", "http-body", "http-body-util", "mime", - "multer", "pin-project-lite", "serde", "tower", @@ -206,17 +204,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "axum_garde" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8405250c4cc6556ae1eb38ef47036cd1779d1c3d0c4acd338d1e6dc918a1f24" -dependencies = [ - "axum", - "garde", - "thiserror 1.0.69", -] - [[package]] name = "backtrace" version = "0.3.74" @@ -334,9 +321,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.20.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -401,20 +388,11 @@ dependencies = [ "serde_json", ] -[[package]] -name = "castaway" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" -dependencies = [ - "rustversion", -] - [[package]] name = "cc" -version = "1.2.2" +version = "1.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" +checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333" dependencies = [ "jobserver", "libc", @@ -450,9 +428,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "num-traits", ] @@ -463,19 +441,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" -[[package]] -name = "compact_str" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "ryu", - "static_assertions", -] - [[package]] name = "cpufeatures" version = "0.2.16" @@ -496,18 +461,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -524,9 +489,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" @@ -697,15 +662,15 @@ checksum = "9afc2bd4d5a73106dd53d10d73d3401c2f32730ba2c0b93ddb888a8983680471" [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdeflate" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ "simd-adler32", ] @@ -728,9 +693,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "foldhash" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" [[package]] name = "form_urlencoded" @@ -743,20 +708,18 @@ dependencies = [ [[package]] name = "freedit" -version = "0.7.6-rc.0" +version = "0.7.6-rc.1" dependencies = [ "ammonia", "atom_syndication", "axum", "axum-extra", - "axum_garde", "basic-toml", "bincode 2.0.0-rc.3", "cached", "captcha", "data-encoding", "fast2s", - "garde", "http", "identicon", "image", @@ -773,7 +736,6 @@ dependencies = [ "reqwest", "ring", "rinja", - "rinja_axum", "rss", "rust-stemmers", "serde", @@ -782,7 +744,7 @@ dependencies = [ "stop-words", "syntect", "tantivy", - "thiserror 2.0.4", + "thiserror 2.0.9", "tikv-jemallocator", "tokio", "tower", @@ -790,6 +752,7 @@ dependencies = [ "tracing", "tracing-subscriber", "unicode-segmentation", + "validator", "whichlang", ] @@ -871,28 +834,6 @@ dependencies = [ "byteorder", ] -[[package]] -name = "garde" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a3233677ea1554a48235d81bb59d2a41654969a8e29a1316c48105fd1701693" -dependencies = [ - "compact_str", - "garde_derive", - "smallvec", -] - -[[package]] -name = "garde_derive" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8796f322e43105351a7ec35148807b32b5b6058a539656dafe4a5b456d5ca41f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "generic-array" version = "0.14.7" @@ -1085,9 +1026,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", @@ -1106,9 +1047,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", "http", @@ -1312,9 +1253,9 @@ dependencies = [ [[package]] name = "img-parts" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd653b443fbb9271d937a4b2c1c7489af95c284a56f84d76bbd00eac857cb1c" +checksum = "dfded0de32cc78ecad0061b3c6a263cec6bce298fc1e670a4926b6723664ed87" dependencies = [ "bytes", "crc32fast", @@ -1379,9 +1320,15 @@ dependencies = [ [[package]] name = "jiff" -version = "0.1.15" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db69f08d4fb10524cacdb074c10b296299d71274ddbc830a8ee65666867002e9" +checksum = "943611a469f78ab9afdac9022e473a80fca16a9deca6c5be3eb566d872231e76" +dependencies = [ + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] [[package]] name = "jobserver" @@ -1394,9 +1341,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.74" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ "once_cell", "wasm-bindgen", @@ -1422,9 +1369,9 @@ checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" [[package]] name = "libc" -version = "0.2.167" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libm" @@ -1524,9 +1471,9 @@ dependencies = [ [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "measure_time" @@ -1577,9 +1524,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", "simd-adler32", @@ -1598,9 +1545,9 @@ dependencies = [ [[package]] name = "mozjpeg" -version = "0.10.11" +version = "0.10.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55571bce4f12d80ceb4296526e7614f796df72daaaac85f265ab732fa47b7bc9" +checksum = "f1d4203b31d5e3069059a88657add5d88da8fac3288a6442dc7e2a7131f2cce3" dependencies = [ "arrayvec", "bytemuck", @@ -1706,9 +1653,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.5" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] @@ -1777,7 +1724,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.7", + "redox_syscall 0.5.8", "smallvec", "windows-targets", ] @@ -1865,9 +1812,9 @@ checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "png" -version = "0.17.14" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -1876,6 +1823,21 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "portable-atomic" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1897,6 +1859,28 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.92" @@ -1926,9 +1910,9 @@ checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" [[package]] name = "quick-xml" -version = "0.37.1" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f22f29bdff3987b4d8632ef95fd6424ec7e4e0a57e2f4fc63e489e75357f6a03" +checksum = "165859e9e55f79d67b96c5d96f4e88b6f2695a1972849c15a6a3f5c59fc2c003" dependencies = [ "encoding_rs", "memchr", @@ -1947,7 +1931,7 @@ dependencies = [ "rustc-hash 2.1.0", "rustls", "socket2", - "thiserror 2.0.4", + "thiserror 2.0.9", "tokio", "tracing", ] @@ -1966,7 +1950,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.4", + "thiserror 2.0.9", "tinyvec", "tracing", "web-time", @@ -1974,9 +1958,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a626c6807713b15cac82a6acaccd6043c9a5408c24baae07611fec3f243da" +checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" dependencies = [ "cfg_aliases", "libc", @@ -1988,9 +1972,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -2066,9 +2050,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -2119,9 +2103,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes", @@ -2147,10 +2131,11 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tokio-rustls", "tokio-socks", + "tower", "tower-service", "url", "wasm-bindgen", @@ -2194,17 +2179,6 @@ dependencies = [ "rinja_derive", ] -[[package]] -name = "rinja_axum" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc64d77bb950f6498d0fc64b028d168fcb4e56ac31b66a8ae05f64d3b0c218b6" -dependencies = [ - "axum-core", - "http", - "rinja", -] - [[package]] name = "rinja_derive" version = "0.3.5" @@ -2270,22 +2244,22 @@ checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.19" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "once_cell", "ring", @@ -2306,9 +2280,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" dependencies = [ "web-time", ] @@ -2326,9 +2300,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" @@ -2353,18 +2327,18 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -2373,9 +2347,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "itoa", "memchr", @@ -2383,6 +2357,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2505,17 +2489,11 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "stop-words" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8500024d809de02ecbf998472b7bed3c4fca380df2be68917f6a473bdb28ddcc" +checksum = "3c6a86be9f7fa4559b7339669e72026eb437f5e9c5a85c207fe1033079033a17" dependencies = [ "serde_json", ] @@ -2560,21 +2538,15 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.90" +version = "2.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.2" @@ -2758,12 +2730,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.14.0" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand", + "getrandom", "once_cell", "rustix", "windows-sys 0.59.0", @@ -2791,11 +2764,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.4" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f49a1853cf82743e3b7950f77e0f4d622ca36cf4317cba00c767838bac8d490" +checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" dependencies = [ - "thiserror-impl 2.0.4", + "thiserror-impl 2.0.9", ] [[package]] @@ -2811,9 +2784,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.4" +version = "2.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8381894bb3efe0c4acac3ded651301ceee58a15d47c2e34885ed1908ad667061" +checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" dependencies = [ "proc-macro2", "quote", @@ -2893,9 +2866,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" dependencies = [ "tinyvec_macros", ] @@ -2935,12 +2908,11 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ "rustls", - "rustls-pki-types", "tokio", ] @@ -2971,14 +2943,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 0.1.2", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -3072,9 +3044,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicase" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" @@ -3145,6 +3117,36 @@ dependencies = [ "serde", ] +[[package]] +name = "validator" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0b4a29d8709210980a09379f27ee31549b73292c87ab9899beee1c0d3be6303" +dependencies = [ + "idna", + "once_cell", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_derive", +] + +[[package]] +name = "validator_derive" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac855a2ce6f843beb229757e6e570a42e837bcb15e5f449dd48d5747d41bf77" +dependencies = [ + "darling", + "once_cell", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "version_check" version = "0.9.5" @@ -3184,9 +3186,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -3195,13 +3197,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn", @@ -3210,9 +3211,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.47" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dfaf8f50e5f293737ee323940c7d8b08a66a95a419223d9f41610ca08b0833d" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", @@ -3223,9 +3224,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3233,9 +3234,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", @@ -3246,15 +3247,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.97" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "web-sys" -version = "0.3.74" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -3576,9 +3577,9 @@ checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" [[package]] name = "zune-jpeg" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index b4943a33..55ce4460 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,29 +1,27 @@ [package] name = "freedit" -version = "0.7.6-rc.0" +version = "0.7.6-rc.1" edition = "2021" license = "MIT License" [dependencies] ammonia = "4.0.0" atom_syndication = { version = "0.12", default-features = false } -axum = { version = "0.7.5", features = ["http1", "http2", "form", "query", "multipart", "tokio"], default-features = false } -axum-extra = { version = "0.9", features = ["typed-header"] } -axum_garde = { version = "0.20.0", default-features = false, features = ["form"] } +axum = { version = "0.8.1", features = ["http1", "http2", "form", "query", "multipart", "tokio"], default-features = false } +axum-extra = { version = "0.10.0", features = ["typed-header"] } basic-toml = "*" bincode = "2.0.0-rc.3" cached = { version = "0.54.0", default-features = false, features = ["proc_macro", "ahash"] } captcha = { git = "https://github.com/freedit-dev/captcha.git", default-features = false } data-encoding = "*" fast2s = "0.3" -garde = { version = "0.20.0", features = ["derive"] } http = "1.2.0" identicon = { git = "https://github.com/freedit-dev/identicon.git", default-features = false } image = { version = "0.25.2", default-features = false, features = ["jpeg", "png", "gif"] } img-parts = "0.3.0" indexmap = "2" jieba-rs = { git = "https://github.com/messense/jieba-rs.git", rev = "b39957e" } -jiff = { version = "0.1.15", default-features = false, features = ["std"] } +jiff = { version = "0.1.19", default-features = false, features = ["std"] } latex2mathml = "0.2.3" mozjpeg = "0.10.11" nanoid = "0.4.0" @@ -33,13 +31,12 @@ regex = "1" reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "socks"] } ring = { version = "0.17", default-features = false } rinja = { version = "0.3.4", default-features = false } -rinja_axum = { version = "0.3.4", default-features = false } rss = { version = "2.0", default-features = false } rust-stemmers = "1.2.0" serde = { version = "1.0", features = ["derive"] } sled = "0.34.7" snailquote = "0.3.1" -stop-words = "0.8.0" +stop-words = "0.8.1" syntect = { version = "5", features = ["regex-fancy", "default-syntaxes", "default-themes", "html"], default-features = false } tantivy = "0.22.0" thiserror = "2" @@ -49,6 +46,7 @@ tower-http = { version = "0.6.1", features = ["fs", "compression-zstd", "trace"] tracing = { version = "0.1", features = ["release_max_level_info", "max_level_info"], default-features = false } tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "smallvec"], default-features = false } unicode-segmentation = "1" +validator = { version = "0.19.0", features = ["derive"] } whichlang = "0.1.0" [target.'cfg(not(target_os = "windows"))'.dependencies] diff --git a/src/app_router.rs b/src/app_router.rs index 40977d40..03311c10 100644 --- a/src/app_router.rs +++ b/src/app_router.rs @@ -53,64 +53,64 @@ pub async fn router() -> Router { .route("/signup", get(signup).post(signup_post)) .route("/signin", get(signin).post(signin_post)) .route("/signout", get(signout)) - .route("/user/:u", get(user)) - .route("/user/:u/follow", get(user_follow)) + .route("/user/{u}", get(user)) + .route("/user/{u}/follow", get(user_follow)) .route("/user/setting", get(user_setting).post(user_setting_post)) .route("/user/avatar", get(user_setting).post(upload_pic_post)) .route("/user/password", get(user_setting).post(user_password_post)) .route("/user/recovery", get(user_setting).post(user_recovery_code)) .route("/user/reset", get(reset).post(reset_post)) .route("/user/list", get(user_list)) - .route("/user/remove/:session_id", get(remove_session)) - .route("/role/:id/:uid", get(user_list).post(role_post)) + .route("/user/remove/{session_id}", get(remove_session)) + .route("/role/{id}/{uid}", get(user_list).post(role_post)) .route("/notification", get(notification)) .route("/admin", get(admin).post(admin_post)) .route("/admin/view", get(admin_view)) .route("/admin/gallery", get(admin_gallery)) - .route("/image/delete/:uid/:img_id", get(image_delete)) - .route("/mod/:iid", get(mod_inn).post(mod_inn_post)) - .route("/mod/feed/:iid", get(mod_inn).post(mod_feed_post)) + .route("/image/delete/{uid}/{img_id}", get(image_delete)) + .route("/mod/{iid}", get(mod_inn).post(mod_inn_post)) + .route("/mod/feed/{iid}", get(mod_inn).post(mod_feed_post)) .route( "/mod/inn_icon", get(mod_inn).post(upload_pic_post.layer(DefaultBodyLimit::max(UPLOAD_LIMIT))), ) - .route("/mod/:iid/:pid/lock", get(post_lock)) - .route("/mod/:iid/:pid/hide", get(post_hide)) - .route("/mod/:iid/:pid/pin", get(post_pin)) + .route("/mod/{iid}/{pid}/lock", get(post_lock)) + .route("/mod/{iid}/{pid}/hide", get(post_hide)) + .route("/mod/{iid}/{pid}/pin", get(post_pin)) .route("/inn/list", get(inn_list)) - .route("/inn/tag/:tag", get(tag)) - .route("/inn/:i", get(inn)) - .route("/inn/:i/join", get(inn_join)) - .route("/inn/:i/atom.xml", get(inn_feed)) - .route("/post/:iid/:pid", get(post).post(comment_post)) - .route("/post/:iid/:pid/:cid/delete", get(comment_delete)) - .route("/post/:iid/:pid/:cid/hide", get(comment_hide)) - .route("/post/edit/:pid", get(edit_post).post(edit_post_post)) - .route("/post/:iid/:pid/upvote", get(post_upvote)) - .route("/post/:iid/:pid/downvote", get(post_downvote)) - .route("/post/:iid/:pid/delete", get(post_delete)) - .route("/post/:iid/:pid/:cid/upvote", get(comment_upvote)) - .route("/post/:iid/:pid/:cid/downvote", get(comment_downvote)) + .route("/inn/tag/{tag}", get(tag)) + .route("/inn/{i}", get(inn)) + .route("/inn/{i}/join", get(inn_join)) + .route("/inn/{i}/atom.xml", get(inn_feed)) + .route("/post/{iid}/{pid}", get(post).post(comment_post)) + .route("/post/{iid}/{pid}/{cid}/delete", get(comment_delete)) + .route("/post/{iid}/{pid}/{cid}/hide", get(comment_hide)) + .route("/post/edit/{pid}", get(edit_post).post(edit_post_post)) + .route("/post/{iid}/{pid}/upvote", get(post_upvote)) + .route("/post/{iid}/{pid}/downvote", get(post_downvote)) + .route("/post/{iid}/{pid}/delete", get(post_delete)) + .route("/post/{iid}/{pid}/{cid}/upvote", get(comment_upvote)) + .route("/post/{iid}/{pid}/{cid}/downvote", get(comment_downvote)) .route("/preview", get(post).post(preview)) - .route("/solo/user/:u", get(solo_list).post(solo_post)) - .route("/solo/:sid/like", get(solo_like)) - .route("/solo/:sid/delete", get(solo_delete)) - .route("/solo/:sid", get(solo)) + .route("/solo/user/{u}", get(solo_list).post(solo_post)) + .route("/solo/{sid}/like", get(solo_like)) + .route("/solo/{sid}/delete", get(solo_delete)) + .route("/solo/{sid}", get(solo)) .route( "/upload", get(upload).post(upload_post.layer(DefaultBodyLimit::max(UPLOAD_LIMIT))), ) - .route("/gallery/:uid", get(gallery)) - .route("/feed/:uid", get(feed)) + .route("/gallery/{uid}", get(gallery)) + .route("/feed/{uid}", get(feed)) .route("/feed/add", get(feed_add).post(feed_add_post)) .route("/feed/update", get(feed_update)) - .route("/feed/star/:item_id", get(feed_star)) - .route("/feed/subscribe/:uid/:item_id", get(feed_subscribe)) - .route("/feed/read/:item_id", get(feed_read)) + .route("/feed/star/{item_id}", get(feed_star)) + .route("/feed/subscribe/{uid}/{item_id}", get(feed_subscribe)) + .route("/feed/read/{item_id}", get(feed_read)) .route("/search", get(search)) - .route("/message/:uid", get(message).post(message_post)) + .route("/message/{uid}", get(message).post(message_post)) .route("/key", get(key).post(key_post)) - .route("/inbox/:mid", get(inbox)); + .route("/inbox/{mid}", get(inbox)); let router_static = Router::new() .route("/static/style.css", get(style)) diff --git a/src/controller/admin.rs b/src/controller/admin.rs index 62080d2e..cb9f0c4f 100644 --- a/src/controller/admin.rs +++ b/src/controller/admin.rs @@ -2,7 +2,7 @@ use super::{ db_utils::{get_range, ivec_to_u32, set_one_with_key, u8_slice_to_u32, IterType}, fmt::{clean_html, ts_to_date}, inn::ParamsTag, - meta_handler::{PageData, ParamsPage}, + meta_handler::{into_response, PageData, ParamsPage, ValidatedForm}, user::Role, Claim, Feed, FormPost, Item, SiteConfig, }; @@ -14,12 +14,10 @@ use crate::{ use axum::{ extract::Query, response::{IntoResponse, Redirect}, - Form, }; use axum_extra::{headers::Cookie, TypedHeader}; -use axum_garde::WithValidation; use bincode::config::standard; -use rinja_axum::{into_response, Template}; +use rinja::Template; use serde::Deserialize; use snailquote::unescape; @@ -350,7 +348,7 @@ pub(crate) async fn admin( /// `POST /admin` pub(crate) async fn admin_post( cookie: Option>, - WithValidation(input): WithValidation>, + ValidatedForm(input): ValidatedForm, ) -> Result { let cookie = cookie.ok_or(AppError::NonLogin)?; let claim = Claim::get(&DB, &cookie, &input).ok_or(AppError::NonLogin)?; @@ -358,7 +356,7 @@ pub(crate) async fn admin_post( return Err(AppError::Unauthorized); } - let mut site_config = input.into_inner(); + let mut site_config = input; site_config.spam_regex = match site_config.spam_regex { Some(s) if !s.is_empty() => { if let Err(e) = regex::Regex::new(&s) { diff --git a/src/controller/feed.rs b/src/controller/feed.rs index 25fa1026..98d66bd1 100644 --- a/src/controller/feed.rs +++ b/src/controller/feed.rs @@ -5,7 +5,7 @@ use super::{ }, fmt::{clean_html, ts_to_date}, inn::inn_add_index, - meta_handler::{get_referer, PageData, ParamsPage}, + meta_handler::{get_referer, into_response, PageData, ParamsPage}, Claim, Inn, Post, PostContent, PostStatus, SiteConfig, User, }; use crate::{ @@ -24,10 +24,9 @@ use axum_extra::{ TypedHeader, }; use cached::proc_macro::cached; -use garde::Validate; use jiff::{fmt::rfc2822, Timestamp}; use reqwest::Client; -use rinja_axum::{into_response, Template}; +use rinja::Template; use serde::Deserialize; use sled::Db; use std::{collections::HashSet, time::Duration}; @@ -36,6 +35,7 @@ use std::{ sync::LazyLock, }; use tracing::{error, warn}; +use validator::Validate; struct SourceItem { link: String, @@ -477,13 +477,13 @@ pub(crate) async fn feed_add( /// Form data: `/feed/add` #[derive(Deserialize, Validate)] pub(crate) struct FormFeedAdd { - #[garde(length(max = 256))] + #[validate(length(max = 256))] url: String, - #[garde(length(max = 256))] + #[validate(length(max = 256))] folder: String, - #[garde(length(max = 256))] + #[validate(length(max = 256))] new_folder: String, - #[garde(skip)] + #[validate(skip)] is_public: bool, } diff --git a/src/controller/inn.rs b/src/controller/inn.rs index eafb6ccc..a755ae52 100644 --- a/src/controller/inn.rs +++ b/src/controller/inn.rs @@ -20,7 +20,7 @@ use super::{ feed::{inn_feed_to_post, update}, fmt::{clean_html, md2html, ts_to_date}, incr_id, - meta_handler::{PageData, ParamsPage}, + meta_handler::{into_response, PageData, ParamsPage, ValidatedForm}, notification::{add_notification, mark_read, NtType}, user::{InnRole, Role}, Claim, Comment, Feed, FormPost, Inn, InnType, Post, PostContent, PostStatus, SiteConfig, User, @@ -33,17 +33,18 @@ use axum::{ Form, }; use axum_extra::{headers::Cookie, TypedHeader}; -use axum_garde::WithValidation; use bincode::config::standard; use cached::proc_macro::cached; -use garde::Validate; use jiff::Timestamp; -use rinja::filters::{escape, Html}; -use rinja_axum::{into_response, Template}; +use rinja::{ + filters::{escape, Html}, + Template, +}; use serde::Deserialize; use sled::{transaction::ConflictableTransactionError, Transactional}; use sled::{Batch, Db}; use std::collections::{BTreeSet, HashMap, HashSet}; +use validator::Validate; /// Page data: `inn_create.html` #[derive(Template)] @@ -115,19 +116,19 @@ pub(crate) async fn mod_inn( /// Form data: `/mod/:iid` inn create/edit page #[derive(Deserialize, Validate)] pub(crate) struct FormInn { - #[garde(length(min = 1, max = 64))] + #[validate(length(min = 1, max = 64))] inn_name: String, - #[garde(length(min = 1, max = 512))] + #[validate(length(min = 1, max = 512))] about: String, - #[garde(length(min = 1, max = 65535))] + #[validate(length(min = 1, max = 65535))] description: String, - #[garde(length(min = 1, max = 128))] + #[validate(length(min = 1, max = 128))] topics: String, - #[garde(skip)] + #[validate(skip)] inn_type: u8, - #[garde(skip)] + #[validate(skip)] early_birds: u32, - #[garde(skip)] + #[validate(skip)] limit_edit_seconds: u32, } @@ -137,7 +138,7 @@ pub(crate) struct FormInn { pub(crate) async fn mod_inn_post( cookie: Option>, Path(mut iid): Path, - WithValidation(input): WithValidation>, + ValidatedForm(input): ValidatedForm, ) -> Result { let inn_name = clean_html(&input.inn_name); if !is_valid_name(&inn_name) { @@ -650,12 +651,11 @@ fn inn_rm_index(db: &Db, iid: u32, pid: u32) -> Result { pub(crate) async fn edit_post_post( cookie: Option>, Path(old_pid): Path, - WithValidation(input): WithValidation>, + ValidatedForm(input): ValidatedForm, ) -> Result { let cookie = cookie.ok_or(AppError::NonLogin)?; let site_config = SiteConfig::get(&DB)?; let claim = Claim::get(&DB, &cookie, &site_config).ok_or(AppError::NonLogin)?; - let input = input.into_inner(); let is_draft = input.is_draft.unwrap_or_default(); let delete_draft = input.delete_draft.unwrap_or_default(); @@ -1746,7 +1746,7 @@ pub(crate) async fn post( /// Form data: `/inn/:iid/:pid/` comment create #[derive(Deserialize, Validate)] pub(crate) struct FormComment { - #[garde(length(min = 1, max = 10000))] + #[validate(length(min = 1, max = 10000))] content: String, } @@ -1754,13 +1754,12 @@ pub(crate) struct FormComment { pub(crate) async fn comment_post( cookie: Option>, Path((iid, pid)): Path<(u32, u32)>, - WithValidation(input): WithValidation>, + ValidatedForm(input): ValidatedForm, ) -> Result { let site_config = SiteConfig::get(&DB)?; let claim = cookie .and_then(|cookie| Claim::get(&DB, &cookie, &site_config)) .ok_or(AppError::NonLogin)?; - let input = input.into_inner(); if let Some(spam_regex) = &site_config.spam_regex { let re = regex::Regex::new(spam_regex).unwrap(); @@ -1896,7 +1895,7 @@ struct PagePreview<'a> { /// `POST /preview` pub(crate) async fn preview( - WithValidation(input): WithValidation>, + ValidatedForm(input): ValidatedForm, ) -> Result { let site_config = SiteConfig::get(&DB)?; let page_data = PageData::new("preview", &site_config, None, false); diff --git a/src/controller/message.rs b/src/controller/message.rs index 85edb9ea..b54d7d74 100644 --- a/src/controller/message.rs +++ b/src/controller/message.rs @@ -4,7 +4,7 @@ use axum::{ Form, }; use axum_extra::{headers::Cookie, TypedHeader}; -use rinja_axum::{into_response, Template}; +use rinja::Template; use serde::Deserialize; use crate::controller::filters; @@ -12,7 +12,7 @@ use crate::{controller::fmt::clean_html, error::AppError, DB}; use super::{ db_utils::{get_one, incr_id, u32_to_ivec, u8_slice_to_u32}, - meta_handler::PageData, + meta_handler::{into_response, PageData}, notification::{add_notification, mark_read, NtType}, Claim, SiteConfig, User, }; diff --git a/src/controller/meta_handler.rs b/src/controller/meta_handler.rs index e87dd139..39503d1d 100644 --- a/src/controller/meta_handler.rs +++ b/src/controller/meta_handler.rs @@ -3,16 +3,20 @@ use std::sync::LazyLock; use super::{db_utils::u32_to_ivec, fmt::md2html, Claim, SiteConfig}; use crate::{controller::filters, error::AppError, DB}; use axum::{ + extract::{rejection::FormRejection, FromRequest, Request}, http::{HeaderMap, HeaderValue, Uri}, response::{IntoResponse, Redirect, Response}, + Form, }; use axum_extra::{ headers::{Cookie, Referer}, TypedHeader, }; use http::{HeaderName, StatusCode}; -use rinja_axum::{into_response, Template}; +use rinja::Template; +use serde::de::DeserializeOwned; use tracing::error; +use validator::Validate; #[derive(Template)] #[template(path = "error.html", escape = "none")] @@ -221,3 +225,42 @@ pub(super) struct ParamsPage { pub(super) n: usize, pub(super) is_desc: bool, } + +#[derive(Debug, Clone, Copy, Default)] +pub(crate) struct ValidatedForm(pub T); + +impl FromRequest for ValidatedForm +where + T: DeserializeOwned + Validate, + S: Send + Sync, + Form: FromRequest, +{ + type Rejection = AppError; + + async fn from_request(req: Request, state: &S) -> Result { + let Form(value) = Form::::from_request(req, state).await?; + value.validate()?; + Ok(ValidatedForm(value)) + } +} + +/// Render a [`Template`] into a [`Response`], or render an error page. +pub(crate) fn into_response(tmpl: &T) -> Response { + try_into_response(tmpl) + .map_err(|err| axum::response::ErrorResponse::from(err.to_string())) + .into_response() +} + +/// Try to render a [`Template`] into a [`Response`]. +pub(crate) fn try_into_response( + tmpl: &T, +) -> Result { + let value = tmpl.render()?.into(); + Response::builder() + .header( + http::header::CONTENT_TYPE, + http::header::HeaderValue::from_static(T::MIME_TYPE), + ) + .body(value) + .map_err(|err| rinja::Error::Custom(err.into())) +} diff --git a/src/controller/mod.rs b/src/controller/mod.rs index b877f005..c1951a17 100644 --- a/src/controller/mod.rs +++ b/src/controller/mod.rs @@ -139,11 +139,11 @@ use crate::error::AppError; use ::tantivy::TantivyDocument; use bincode::config::standard; use bincode::{Decode, Encode}; -use garde::Validate; use jiff::{Timestamp, ToSpan}; use serde::{Deserialize, Serialize}; use sled::Db; use std::fmt::Display; +use validator::Validate; /// user /// @@ -386,17 +386,17 @@ impl ToDoc for Post { /// Form data: `/inn/:iid/post/:pid` post create/edit page #[derive(Debug, Default, Deserialize, Validate, Encode, Decode)] pub(super) struct FormPost { - #[garde(skip)] + #[validate(skip)] iid: u32, - #[garde(length(min = 1, max = 256))] + #[validate(length(min = 1, max = 256))] title: String, - #[garde(length(min = 1, max = 128))] + #[validate(length(min = 1, max = 128))] tags: String, - #[garde(length(min = 1, max = 65535))] + #[validate(length(min = 1, max = 65535))] content: String, - #[garde(skip)] + #[validate(skip)] is_draft: Option, - #[garde(skip)] + #[validate(skip)] delete_draft: Option, } @@ -451,40 +451,40 @@ impl ToDoc for Item { /// Go to source code to see default value: [SiteConfig::default()] #[derive(Serialize, Deserialize, Encode, Decode, Validate, Debug)] pub(super) struct SiteConfig { - #[garde(length(max = 64))] + #[validate(length(max = 64))] site_name: String, // domain only used for inn feed - #[garde(skip)] + #[validate(skip)] domain: String, - #[garde(length(max = 5120))] + #[validate(length(max = 5120))] description: String, - #[garde(skip)] + #[validate(skip)] read_only: bool, - #[garde(range(max = 32))] + #[validate(range(max = 32))] inn_mod_max: usize, - #[garde(range(max = 256))] + #[validate(range(max = 256))] title_max_length: usize, - #[garde(range(max = 65535))] + #[validate(range(max = 65535))] article_max_length: usize, - #[garde(range(max = 65535))] + #[validate(range(max = 65535))] comment_max_length: usize, - #[garde(range(max = 3600))] + #[validate(range(max = 3600))] solo_interval: i64, - #[garde(range(max = 3600))] + #[validate(range(max = 3600))] post_interval: i64, - #[garde(range(max = 3600))] + #[validate(range(max = 3600))] comment_interval: i64, - #[garde(range(max = 100))] + #[validate(range(max = 100))] per_page: usize, - #[garde(skip)] + #[validate(skip)] captcha_difficulty: String, - #[garde(skip)] + #[validate(skip)] captcha_name: String, - #[garde(skip)] + #[validate(skip)] home_page: u8, - #[garde(skip)] + #[validate(skip)] spam_regex: Option, - #[garde(length(max = 16))] + #[validate(length(max = 16))] lang: String, } diff --git a/src/controller/notification.rs b/src/controller/notification.rs index 0c0dd377..3644d1b6 100644 --- a/src/controller/notification.rs +++ b/src/controller/notification.rs @@ -1,6 +1,6 @@ use super::{ get_ids_by_prefix, get_one, incr_id, - meta_handler::PageData, + meta_handler::{into_response, PageData}, u32_to_ivec, u8_slice_to_u32, user::{InnRole, Role}, Claim, Comment, Inn, Post, SiteConfig, Solo, User, @@ -9,7 +9,7 @@ use crate::{controller::filters, error::AppError, DB}; use axum::{extract::Query, response::IntoResponse}; use axum_extra::{headers::Cookie, TypedHeader}; use bincode::config::standard; -use rinja_axum::{into_response, Template}; +use rinja::Template; use serde::Deserialize; use sled::{Db, IVec}; use snailquote::unescape; diff --git a/src/controller/solo.rs b/src/controller/solo.rs index c2a7f118..611888bb 100644 --- a/src/controller/solo.rs +++ b/src/controller/solo.rs @@ -5,7 +5,7 @@ use super::{ }, fmt::{md2html, ts_to_date}, get_ids_by_prefix, get_one, incr_id, ivec_to_u32, - meta_handler::{get_referer, PageData, ParamsPage}, + meta_handler::{get_referer, into_response, PageData, ParamsPage, ValidatedForm}, notification::{add_notification, mark_read, NtType}, u32_to_ivec, u8_slice_to_u32, user::Role, @@ -15,28 +15,26 @@ use crate::{controller::filters, error::AppError, DB}; use axum::{ extract::{Path, Query}, response::{IntoResponse, Redirect}, - Form, }; use axum_extra::{ headers::{Cookie, Referer}, TypedHeader, }; -use axum_garde::WithValidation; -use garde::Validate; use jiff::Timestamp; -use rinja_axum::{into_response, Template}; +use rinja::Template; use serde::Deserialize; use sled::Db; use tracing::warn; +use validator::Validate; /// Form data: `/solo/user/:uid` solo create. #[derive(Deserialize, Validate)] pub(crate) struct FormSolo { - #[garde(length(min = 1, max = 1000))] + #[validate(length(min = 1, max = 1000))] content: String, - #[garde(skip)] + #[validate(skip)] solo_type: u32, - #[garde(skip)] + #[validate(skip)] reply_to: u32, } @@ -381,7 +379,7 @@ fn get_solos_by_uids( /// `POST /solo/user/:uid` solo page pub(crate) async fn solo_post( cookie: Option>, - WithValidation(input): WithValidation>, + ValidatedForm(input): ValidatedForm, ) -> Result { let site_config = SiteConfig::get(&DB)?; let claim = cookie @@ -400,7 +398,6 @@ pub(crate) async fn solo_post( return Err(AppError::WriteInterval); } - let input = input.into_inner(); let solo_type = SoloType::from(input.solo_type); let uid = claim.uid; diff --git a/src/controller/tantivy.rs b/src/controller/tantivy.rs index c7ff0e7b..906efdae 100644 --- a/src/controller/tantivy.rs +++ b/src/controller/tantivy.rs @@ -5,7 +5,7 @@ use axum_extra::{headers::Cookie, TypedHeader}; use bincode::config::standard; use indexmap::IndexSet; use jieba_rs::{Jieba, TokenizeMode}; -use rinja_axum::{into_response, Template}; +use rinja::Template; use rust_stemmers::{Algorithm, Stemmer}; use serde::Deserialize; use sled::{Batch, Db}; @@ -34,7 +34,7 @@ use crate::{ use super::{ db_utils::{get_one, u32_to_ivec, u8_slice_to_u32}, fmt::ts_to_date, - meta_handler::PageData, + meta_handler::{into_response, PageData}, Claim, Comment, Item, Post, PostStatus, SiteConfig, Solo, User, }; diff --git a/src/controller/upload.rs b/src/controller/upload.rs index 7255b922..6fce06a3 100644 --- a/src/controller/upload.rs +++ b/src/controller/upload.rs @@ -2,7 +2,7 @@ use super::{ db_utils::{u8_slice_to_u32, IterType}, incr_id, inn::ParamsTag, - meta_handler::{get_referer, PageData}, + meta_handler::{get_referer, into_response, PageData}, notification::{add_notification, NtType}, u32_to_ivec, user::{InnRole, Role}, @@ -22,7 +22,7 @@ use image::{imageops::FilterType, ImageFormat}; use img_parts::{DynImage, ImageEXIF}; use mozjpeg::{ColorSpace, Compress, ScanMode}; use ring::digest::{Context, SHA1_FOR_LEGACY_USE_ONLY}; -use rinja_axum::{into_response, Template}; +use rinja::Template; use serde::Deserialize; use sled::Batch; use tokio::fs::{self, remove_file}; @@ -62,7 +62,7 @@ pub(crate) async fn upload_pic_post( target = "/user/setting".to_string(); format!("{}/{}.png", &CONFIG.avatars_path, claim.uid) } - _ => unreachable!(), + _ => return Err(AppError::NotFound), }; if let Some(field) = multipart.next_field().await.unwrap() { diff --git a/src/controller/user.rs b/src/controller/user.rs index a233920a..03a331d9 100644 --- a/src/controller/user.rs +++ b/src/controller/user.rs @@ -8,7 +8,7 @@ use super::{ filters, fmt::{clean_html, ts_to_date}, get_ids_by_prefix, get_one, incr_id, - meta_handler::{PageData, ParamsPage}, + meta_handler::{into_response, PageData, ParamsPage, ValidatedForm}, notification::{add_notification, NtType}, u32_to_ivec, u8_slice_to_u32, Claim, Inn, InnType, SiteConfig, User, }; @@ -20,25 +20,24 @@ use axum::{ response::{IntoResponse, Redirect}, }; use axum_extra::{headers::Cookie, TypedHeader}; -use axum_garde::WithValidation; use bincode::config::standard; use captcha::{ filters::{Cow, Noise, Wave}, Captcha, CaptchaName, Difficulty, Geometry, }; use data_encoding::BASE64; -use garde::Validate; use identicon::Identicon; use jiff::Timestamp; use ring::{ pbkdf2, rand::{self, SecureRandom}, }; -use rinja_axum::{into_response, Template}; +use rinja::Template; use serde::Deserialize; use sled::Db; use std::{cmp::Ordering, fmt::Display, num::NonZeroU32, time::Duration}; use tokio::time::sleep; +use validator::Validate; /// Page data: `user.html` #[derive(Template)] @@ -529,7 +528,7 @@ pub(crate) async fn role_post( } 10 } - _ => unreachable!(), + _ => return Err(AppError::Unauthorized), }; if old_inn_role != Some(inn_role.into()) { @@ -565,7 +564,7 @@ pub(crate) async fn role_post( "Senior" => 100, "Standard" => 10, "Banned" => 0, - _ => unreachable!(), + _ => return Err(AppError::Unauthorized), }; if user.role != role { @@ -586,15 +585,15 @@ pub(crate) async fn role_post( /// Form data: `/user/setting` #[derive(Deserialize, Validate)] pub(crate) struct FormUser { - #[garde(length(min = 1, max = 32))] + #[validate(length(min = 1, max = 32))] username: String, - #[garde(length(max = 1024))] + #[validate(length(max = 1024))] about: String, - #[garde(length(max = 256))] + #[validate(length(max = 256))] url: String, - #[garde(skip)] + #[validate(skip)] home_page: u8, - #[garde(skip)] + #[validate(skip)] lang: String, } @@ -679,13 +678,11 @@ pub(crate) async fn reset( /// Form data: `/user/setting` #[derive(Deserialize, Validate)] -#[garde(allow_unvalidated)] pub(crate) struct FormReset { username: String, recovery_code: String, - #[garde(matches(password2))] password: String, - #[garde(length(min = 7))] + #[validate(length(min = 7))] password2: String, } @@ -694,6 +691,9 @@ pub(crate) async fn reset_post( cookie: Option>, Form(input): Form, ) -> Result { + if input.password != input.password2 { + return Err(AppError::Custom("Passwords do not match".to_string())); + } let site_config = SiteConfig::get(&DB)?; if let Some(cookie) = cookie { @@ -737,7 +737,7 @@ pub(crate) async fn remove_session( /// `POST /user/setting` pub(crate) async fn user_setting_post( cookie: Option>, - WithValidation(input): WithValidation>, + ValidatedForm(input): ValidatedForm, ) -> Result { let cookie = cookie.ok_or(AppError::NonLogin)?; let site_config = SiteConfig::get(&DB)?; @@ -788,19 +788,21 @@ pub(crate) async fn user_setting_post( /// Form data: `/user/setting` #[derive(Deserialize, Validate)] pub(crate) struct FormPassword { - #[garde(skip)] + #[validate(skip)] old_password: String, - #[garde(matches(password2))] password: String, - #[garde(length(min = 7))] + #[validate(length(min = 7))] password2: String, } /// `POST /user/password` pub(crate) async fn user_password_post( cookie: Option>, - WithValidation(input): WithValidation>, + ValidatedForm(input): ValidatedForm, ) -> Result { + if input.password != input.password2 { + return Err(AppError::Custom("Passwords do not match".to_string())); + } let cookie = cookie.ok_or(AppError::NonLogin)?; let site_config = SiteConfig::get(&DB)?; let claim = Claim::get(&DB, &cookie, &site_config).ok_or(AppError::NonLogin)?; @@ -882,13 +884,11 @@ pub(crate) async fn signin_post(Form(input): Form) -> impl IntoRespo /// Form data: `/signup` #[derive(Deserialize, Validate)] -#[garde(allow_unvalidated)] pub(crate) struct FormSignup { - #[garde(length(min = 1, max = 32))] + #[validate(length(min = 1, max = 32))] username: String, - #[garde(matches(password2))] password: String, - #[garde(length(min = 7))] + #[validate(length(min = 7))] password2: String, captcha_id: String, captcha_value: String, @@ -915,7 +915,7 @@ pub(crate) async fn signup() -> Result { "Easy" => Difficulty::Easy, "Medium" => Difficulty::Medium, "Hard" => Difficulty::Hard, - _ => unreachable!(), + _ => return Err(AppError::NotFound), }; let captcha = match site_config.captcha_name.as_str() { @@ -923,7 +923,7 @@ pub(crate) async fn signup() -> Result { "Lucy" => captcha::by_name(captcha_difficulty, CaptchaName::Lucy), "Mila" => captcha::by_name(captcha_difficulty, CaptchaName::Mila), "Digits" => captcha_digits(), - _ => unreachable!(), + _ => return Err(AppError::NotFound), }; let captcha_id = generate_nanoid_ttl(60); @@ -960,8 +960,11 @@ fn captcha_digits() -> Captcha { /// `POST /signup` pub(crate) async fn signup_post( - WithValidation(input): WithValidation>, + ValidatedForm(input): ValidatedForm, ) -> Result { + if input.password != input.password2 { + return Err(AppError::Custom("Passwords do not match".to_string())); + } let username = clean_html(&input.username); if !is_valid_name(&username) { return Err(AppError::NameInvalid); @@ -1043,14 +1046,14 @@ struct PageShowRecovery<'a> { /// Form data: `/user/recovery` #[derive(Deserialize, Validate)] pub(crate) struct FormRecoverySet { - #[garde(length(min = 7))] + #[validate(length(min = 7))] password: String, } /// `POST /user/recovery` pub(crate) async fn user_recovery_code( cookie: Option>, - WithValidation(input): WithValidation>, + ValidatedForm(input): ValidatedForm, ) -> Result { let cookie = cookie.ok_or(AppError::NonLogin)?; let site_config = SiteConfig::get(&DB)?; diff --git a/src/error.rs b/src/error.rs index 8f626f81..6ba403c3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -46,7 +46,7 @@ pub enum AppError { #[error("The site is under maintenance. It is read only at the moment")] ReadOnly, #[error(transparent)] - ValidationError(#[from] garde::Error), + ValidationError(#[from] validator::ValidationErrors), #[error(transparent)] AxumFormRejection(#[from] axum::extract::rejection::FormRejection), #[error("Invalid feed link")] diff --git a/templates/notification.html b/templates/notification.html index b48e2b75..e1749a61 100644 --- a/templates/notification.html +++ b/templates/notification.html @@ -29,13 +29,13 @@

diff --git a/templates/user_list.html b/templates/user_list.html index ab26081a..6cd13f77 100644 --- a/templates/user_list.html +++ b/templates/user_list.html @@ -109,14 +109,14 @@

@@ -133,10 +133,10 @@