diff --git a/Cargo.lock b/Cargo.lock index a04d933..84fb2e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,7 +16,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -400,6 +400,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + [[package]] name = "brotli" version = "8.0.2" @@ -439,6 +448,12 @@ version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "byteorder-lite" version = "0.1.0" @@ -478,6 +493,17 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" version = "0.4.44" @@ -497,10 +523,16 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common", + "crypto-common 0.1.7", "inout", ] +[[package]] +name = "cmov" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" + [[package]] name = "color_quant" version = "1.1.0" @@ -552,6 +584,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + [[package]] name = "constant_time_eq" version = "0.3.1" @@ -593,6 +631,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.4.0" @@ -658,6 +705,24 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + [[package]] name = "deflate64" version = "0.1.12" @@ -690,11 +755,23 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "crypto-common 0.1.7", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "const-oid", + "crypto-common 0.2.1", + "ctutils", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -816,6 +893,12 @@ dependencies = [ "zune-inflate", ] +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fastrand" version = "2.4.1" @@ -914,6 +997,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -977,7 +1061,7 @@ checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", ] [[package]] @@ -1001,6 +1085,7 @@ dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", "wasip2", "wasip3", ] @@ -1032,9 +1117,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" dependencies = [ "atomic-waker", "bytes", @@ -1087,7 +1172,16 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", +] + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", ] [[package]] @@ -1135,6 +1229,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.9.0" @@ -1437,16 +1540,6 @@ version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" -[[package]] -name = "iri-string" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "is_terminal_polyfill" version = "1.70.2" @@ -1504,9 +1597,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.97" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ "cfg-if", "futures-util", @@ -1593,7 +1686,7 @@ dependencies = [ "bitflags", "libc", "plain", - "redox_syscall 0.7.4", + "redox_syscall 0.7.5", ] [[package]] @@ -1621,7 +1714,7 @@ dependencies = [ "indexmap", "log", "quick-xml", - "rand", + "rand 0.9.4", "rayon", "serde", "serde_json", @@ -1685,7 +1778,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c60a23ffb90d527e23192f1246b14746e2f7f071cb84476dd879071696c18a4a" dependencies = [ "crc", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -1704,6 +1797,16 @@ dependencies = [ "rayon", ] +[[package]] +name = "md-5" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" +dependencies = [ + "cfg-if", + "digest 0.11.3", +] + [[package]] name = "memchr" version = "2.8.0" @@ -1733,7 +1836,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.61.2", ] @@ -1795,9 +1898,9 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "no_std_io2" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b51ed7824b6e07d354605f4abb3d9d300350701299da96642ee084f5ce631550" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" dependencies = [ "memchr", ] @@ -1873,6 +1976,24 @@ dependencies = [ "autocfg", ] +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", +] + +[[package]] +name = "objc2-system-configuration" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7216bd11cbda54ccabcab84d523dc93b858ec75ecfb3a7d89513fa22464da396" +dependencies = [ + "objc2-core-foundation", +] + [[package]] name = "once_cell" version = "1.21.4" @@ -1981,8 +2102,8 @@ version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ - "digest", - "hmac", + "digest 0.10.7", + "hmac 0.12.1", ] [[package]] @@ -2003,19 +2124,38 @@ dependencies = [ ] [[package]] -name = "pin-project" -version = "1.1.11" +name = "phf" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_shared", + "serde", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf0d9e68100b3a7989b4901972f265cd542e560a3a8a724e1e20322f4d06ce9" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "a990e22f43e84855daf260dded30524ef4a9021cc7541c26540500a50b624389" dependencies = [ "proc-macro2", "quote", @@ -2068,6 +2208,35 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "postgres-protocol" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56201207dac53e2f38e848e31b4b91616a6bb6e0c7205b77718994a7f49e70fc" +dependencies = [ + "base64", + "byteorder", + "bytes", + "fallible-iterator", + "hmac 0.13.0", + "md-5", + "memchr", + "rand 0.10.1", + "sha2 0.11.0", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc729a129e682e8d24170cd30ae1aa01b336b096cbb56df6d534ffec133d186" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", +] + [[package]] name = "potential_utf" version = "0.1.5" @@ -2119,18 +2288,18 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" +checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" dependencies = [ "quote", "syn", @@ -2232,9 +2401,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.39.2" +version = "0.39.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +checksum = "721da970c312655cde9b4ffe0547f20a8494866a4af5ff51f18b7c633d0c870b" dependencies = [ "memchr", ] @@ -2267,7 +2436,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", ] [[package]] @@ -2277,7 +2457,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", ] [[package]] @@ -2289,6 +2469,12 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rav1e" version = "0.8.1" @@ -2316,7 +2502,7 @@ dependencies = [ "num-traits", "paste", "profiling", - "rand", + "rand 0.9.4", "rand_chacha", "simd_helpers", "thiserror", @@ -2395,9 +2581,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" +checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" dependencies = [ "bitflags", ] @@ -2676,8 +2862,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", ] [[package]] @@ -2693,8 +2879,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -2728,6 +2925,12 @@ dependencies = [ "quote", ] +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + [[package]] name = "slab" version = "0.4.12" @@ -2762,6 +2965,17 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "subtle" version = "2.6.1" @@ -2857,6 +3071,7 @@ dependencies = [ "serde", "serde_json", "tokio", + "tokio-postgres", "tokio-util", "tonic", "tonic-prost", @@ -2941,10 +3156,25 @@ dependencies = [ ] [[package]] -name = "tokio" -version = "1.52.1" +name = "tinyvec" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" dependencies = [ "bytes", "libc", @@ -2978,6 +3208,32 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-postgres" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dd8df5ef180f6364759a6f00f7aadda4fbbac86cdee37480826a6ff9f3574ce" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand 0.10.1", + "socket2", + "tokio", + "tokio-util", + "whoami", +] + [[package]] name = "tokio-rustls" version = "0.26.4" @@ -3014,9 +3270,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" dependencies = [ "async-trait", "axum", @@ -3043,9 +3299,9 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1882ac3bf5ef12877d7ed57aad87e75154c11931c2ba7e6cde5e22d63522c734" +checksum = "c68f61875ac5293cf72e6c8cf0158086428c82c37229e98c840878f1706b0322" dependencies = [ "prettyplease", "proc-macro2", @@ -3055,9 +3311,9 @@ dependencies = [ [[package]] name = "tonic-prost" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" dependencies = [ "bytes", "prost", @@ -3066,9 +3322,9 @@ dependencies = [ [[package]] name = "tonic-prost-build" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3144df636917574672e93d0f56d7edec49f90305749c668df5101751bb8f95a" +checksum = "654e5643eff75d7f8c99197ce1440ed19a3474eada74c12bbac488b2cafdae27" dependencies = [ "prettyplease", "proc-macro2", @@ -3101,20 +3357,20 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" dependencies = [ "bitflags", "bytes", "futures-util", "http", "http-body", - "iri-string", "pin-project-lite", "tower", "tower-layer", "tower-service", + "url", ] [[package]] @@ -3179,12 +3435,33 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -3280,6 +3557,15 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + [[package]] name = "wasip2" version = "1.0.3+wasi-0.2.9" @@ -3299,10 +3585,19 @@ dependencies = [ ] [[package]] -name = "wasm-bindgen" -version = "0.2.120" +name = "wasite" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +checksum = "66fe902b4a6b8028a753d5424909b764ccf79b7a209eac9bf97e59cda9f71a42" +dependencies = [ + "wasi 0.14.7+wasi-0.2.4", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", @@ -3313,9 +3608,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.70" +version = "0.4.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" +checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" dependencies = [ "js-sys", "wasm-bindgen", @@ -3323,9 +3618,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.120" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3333,9 +3628,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.120" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ "bumpalo", "proc-macro2", @@ -3346,9 +3641,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.120" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" dependencies = [ "unicode-ident", ] @@ -3389,9 +3684,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.97" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" dependencies = [ "js-sys", "wasm-bindgen", @@ -3403,6 +3698,19 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" +[[package]] +name = "whoami" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "998767ef88740d1f5b0682a9c53c24431453923962269c2db68ee43788c5a40d" +dependencies = [ + "libc", + "libredox", + "objc2-system-configuration", + "wasite", + "web-sys", +] + [[package]] name = "winapi-util" version = "0.1.11" @@ -3817,7 +4125,7 @@ dependencies = [ "deflate64", "flate2", "getrandom 0.3.4", - "hmac", + "hmac 0.12.1", "indexmap", "lzma-rust2", "memchr", diff --git a/Cargo.toml b/Cargo.toml index d15588c..0121b2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ tokio-util = { version = "0.7.18", features = ["io"] } tonic = { version = "0.14.2", features = ["transport"] } tonic-prost = "0.14.2" uuid = { version = "1.20.0", features = ["v4"] } +tokio-postgres = "0.7.17" [build-dependencies] tonic-prost-build = "0.14.2" diff --git a/src/app.rs b/src/app.rs index cf20e75..77c26c0 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,10 +1,16 @@ -use std::{collections::HashMap, io::Cursor, sync::Arc, time::Duration}; +use std::{ + collections::HashMap, + io::Cursor, + path::{Path, PathBuf}, + sync::Arc, + time::Duration, +}; use async_compression::tokio::bufread::BrotliEncoder; use axum::{ Json, Router, body::Body, - extract::{Query, State}, + extract::{DefaultBodyLimit, Query, State}, response::{IntoResponse, Response}, routing::{get, post}, }; @@ -22,7 +28,7 @@ use image::load_from_memory; use log::{error, info, warn}; use redis::{AsyncTypedCommands, Connection, TypedCommands, aio::MultiplexedConnection}; use reqwest::header; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use serde_json::{Value, json}; use tokio::{ io::{AsyncReadExt, BufReader}, @@ -33,6 +39,7 @@ use tokio::{ task::JoinSet, }; +use tokio_postgres::NoTls; use uuid::Uuid; use crate::{ @@ -58,11 +65,11 @@ pub struct AppState { // cached_country_names: Vec<&'static str>, configures: gcm::Configure, repo: Arc>, + // commit external db + postgres: Arc>, redis: RedisPool, // save already fetched path, further calls should fetch from redis instead. fetched: Vec, - // queue - enqueue_tx: Sender, } impl AppState { @@ -349,12 +356,18 @@ struct Signature { email: String, } -#[debug_handler] async fn commit_handler( State(state): State, // request body as multipart/form-data mut payload: Multipart, ) -> impl IntoResponse { + // operate on repo + let op_repo = state + .get_config("GIT_REPO_LOCAL_DEST") + .cloned() + .unwrap_or_default() + .replace("./", ""); + // Extract multipart fields let mut path: Option = None; let mut signature_username: Option = None; @@ -362,6 +375,8 @@ async fn commit_handler( let mut message: Option = None; let mut file_bytes: Option> = None; + let mut ref_message: Option = None; + let mut file_mappings: HashMap> = HashMap::new(); let mut filex_map: HashMap> = HashMap::new(); let mut pathx_map: HashMap = HashMap::new(); @@ -420,7 +435,8 @@ async fn commit_handler( }); } "file" => { - file_bytes = Some(match field.bytes().await { + let _file_bytes = field.bytes().await; + file_bytes = Some(match _file_bytes { Ok(b) => b.to_vec(), Err(e) => { return ( @@ -430,6 +446,17 @@ async fn commit_handler( } }); } + "ref" => { + ref_message = Some(match field.text().await { + Ok(r) => r, + Err(e) => { + return ( + axum::http::StatusCode::BAD_REQUEST, + Json(json!({"error": format!("Failed to read ref field: {}", e)})), + ); + } + }) + } filex if name.starts_with("file") => match field.bytes().await { Ok(fx) => { @@ -495,8 +522,8 @@ async fn commit_handler( // Create signature let signature = Signature { - username: signature_username, - email: signature_email, + username: signature_username.clone(), + email: signature_email.clone(), }; info!("sig: {signature:?}"); @@ -557,6 +584,44 @@ async fn commit_handler( info!("commit success! [{commit_oid}]"); } + // TODO: save to db + + let mut plock = state.postgres.lock().await; + let tx = match plock.transaction().await { + Ok(x) => x, + Err(_) => { + return ( + axum::http::StatusCode::CREATED, + Json( + json!({"result": format!("{commit_oid}"), "reason": "CannotConnect_CommitMayBeLost"}), + ), + ); + } + }; + + let insert_commit = tx.execute("INSERT INTO recovery_commit (commit_hash, commit_user, email, repo, ref) VALUES ($1, $2, $3, $4, $5)", &[ + &commit_oid.to_string(), + &signature_username.clone(), + &signature_email.clone(), + &op_repo, + &ref_message.unwrap_or_default(), + ]).await; + if insert_commit.is_ok() { + match tx.commit().await { + Ok(_) => { + info!("successfully inserted into postgres"); + } + Err(e) => { + error!("commit postgres failed: {e}"); + } + } + } else { + error!("failed to commit to postgres & rolling back ..."); + if let Err(rollback_error) = tx.rollback().await { + error!("failed to rollback, {rollback_error}"); + } + } + ( axum::http::StatusCode::OK, Json(json!({"result": format!("{commit_oid}")})), @@ -712,11 +777,34 @@ async fn commit_file_content( ) -> Result> { let repo_guard = repo.lock().await; - let blob_oid = repo_guard.blob(content)?; + let workdir = repo_guard.workdir().ok_or_else(|| { + git2::Error::from_str("Repository must have a working directory for this strategy") + })?; - let mut index = repo_guard.index()?; + let full_path = if Path::new(path).is_absolute() { + Path::new(path).to_path_buf() + } else { + workdir.join(path) + }; + if let Some(parent) = full_path.parent() { + std::fs::create_dir_all(parent)?; + } + + std::fs::write(&full_path, content)?; let target_ref = format!("refs/heads/{branch}"); + + let relative_git_path = if full_path.is_absolute() { + full_path.strip_prefix(workdir).map_err(|_| { + git2::Error::from_str( + "The provided path is outside of the repository working directory", + ) + })? + } else { + Path::new(path) + }; + let mut index = repo_guard.index()?; + let parent_commit = match repo_guard.find_reference(&target_ref) { Ok(reference) => { let commit = reference.peel_to_commit()?; @@ -726,20 +814,8 @@ async fn commit_file_content( Err(_) => None, }; - index.add(&git2::IndexEntry { - ctime: git2::IndexTime::new(0, 0), - mtime: git2::IndexTime::new(0, 0), - dev: 0, - ino: 0, - mode: 0o100644, - uid: 0, - gid: 0, - file_size: content.len() as u32, - id: blob_oid, - flags: 0, - flags_extended: 0, - path: path.as_bytes().to_vec(), - })?; + index.add_path(relative_git_path)?; + index.write()?; let tree_oid = index.write_tree()?; let tree = repo_guard.find_tree(tree_oid)?; @@ -753,14 +829,17 @@ async fn commit_file_content( let oid = repo_guard.commit( //"refs/heads/master" - Some(&target_ref), - &sig, - &sig, - message, - &tree, - &parents, + None, &sig, &sig, message, &tree, &parents, )?; + repo_guard.reference(&target_ref, oid, true, "api commit log update")?; + + if let Ok(mut head) = repo_guard.head() { + if head.name() == Some(&target_ref) { + repo_guard.set_head(&target_ref)?; + } + } + info!("commit oid: {oid}"); Ok(oid) @@ -774,9 +853,59 @@ async fn commit_multiple_file_contents( branch: String, ) -> Result> { let repo_guard = repo.lock().await; - let mut index = repo_guard.index()?; + let workdir = repo_guard.workdir().ok_or_else(|| { + git2::Error::from_str("Repository must have a working directory for this strategy") + })?; + + // Pre-calculate and normalize paths into a standard vector so we don't have + // path-resolution discrepancies between loops. + // Keeps: (Absolute Path on Disk, Relative Path for Git Index, Content Buffer) + let mut processing_queue: Vec<(PathBuf, PathBuf, &[u8])> = Vec::new(); + + for (raw_path, content) in &file_mapping { + let path_obj = Path::new(raw_path); + + // 1. Compute a clean, guaranteed absolute path for writing to disk + let full_path = if path_obj.is_absolute() { + path_obj.to_path_buf() + } else { + // This handles files starting with "./", "../", or raw filenames safely + workdir.join(path_obj) + }; + + // Canonicalize or cleanly normalize the path syntax relative to the workdir + // to strip out relative quirks like "." or ".." components + let clean_full_path = match full_path.canonicalize() { + Ok(p) => p, + Err(_) => full_path, // Fallback if file doesn't exist yet on disk + }; + + // 2. Compute a strict relative path matching how Git sees the workspace + let relative_git_path = clean_full_path + .strip_prefix(workdir) + .map_err(|_| { + git2::Error::from_str( + "A provided path lives outside the repository working directory", + ) + })? + .to_path_buf(); + + processing_queue.push((clean_full_path, relative_git_path, content)); + } + + // Loop 1: Safely write all processed files to disk first + for (full_path, _, content) in &processing_queue { + if let Some(parent) = full_path.parent() { + std::fs::create_dir_all(parent)?; + } + std::fs::write(full_path, content)?; + } + + let mut index = repo_guard.index()?; let target_ref = format!("refs/heads/{branch}"); + + // Loop 2: Hydrate index from previous branch commit tree let parent_commit = match repo_guard.find_reference(&target_ref) { Ok(reference) => { let commit = reference.peel_to_commit()?; @@ -786,24 +915,15 @@ async fn commit_multiple_file_contents( Err(_) => None, }; - for (_, (path, file)) in file_mapping.iter().enumerate() { - let blob_oid = repo_guard.blob(file.as_slice())?; - index.add(&git2::IndexEntry { - ctime: git2::IndexTime::new(0, 0), - mtime: git2::IndexTime::new(0, 0), - dev: 0, - ino: 0, - mode: 0o100644, - uid: 0, - gid: 0, - file_size: file.len() as u32, - id: blob_oid, - flags: 0, - flags_extended: 0, - path: path.as_bytes().to_vec(), - })?; + // Loop 3: Stage all files using the matching pre-calculated relative path + for (_, relative_git_path, _) in &processing_queue { + index.add_path(relative_git_path)?; } + // Flush changes to .git/index + index.write()?; + + // Create tree layout from updated index state let tree_oid = index.write_tree()?; let tree = repo_guard.find_tree(tree_oid)?; @@ -814,17 +934,20 @@ async fn commit_multiple_file_contents( None => vec![], }; - let oid = repo_guard.commit( - //"refs/heads/master" - Some(&target_ref), - &sig, - &sig, - message, - &tree, - &parents, - )?; + // Commit changes to the object database + let oid = repo_guard.commit(None, &sig, &sig, message, &tree, &parents)?; - info!("commit oid: {oid}"); + // Advance branch reference explicitly + repo_guard.reference(&target_ref, oid, true, "api multi-file commit update")?; + + // Sync active workspace reference state if checking out the branch locally + if let Ok(head) = repo_guard.head() { + if head.name() == Some(&target_ref) { + repo_guard.set_head(&target_ref)?; + } + } + + info!("Multi-file transaction successfully committed. OID: {oid}"); Ok(oid) } @@ -909,6 +1032,102 @@ async fn pull_handler(State(state): State) -> impl IntoResponse { ) } +#[derive(Debug, Serialize, Deserialize)] +struct ReplayPayload { + commit_hashes: Vec, +} + +async fn replay_handler( + State(state): State, + Json(payload): Json, +) -> impl IntoResponse { + let repo = state.repo.clone(); + + let commit_hashes: Vec<&str> = payload.commit_hashes.iter().map(|x| x.as_str()).collect(); + + if let Err(e) = replay(repo, &commit_hashes.as_slice()).await { + return ( + axum::http::StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({"error": e.to_string()})), + ); + } + + ( + axum::http::StatusCode::OK, + Json(json!({"result": "replay completed"})), + ) +} + +async fn replay( + repo: Arc>, + commit_hashes: &[&str], +) -> Result<(), Box> { + let repo_guard = repo.lock().await; + + let initial_head = repo_guard.head()?.peel_to_commit()?; + info!( + "Starting transaction replay. Initial HEAD: {}", + initial_head.id() + ); + + for (index, hash) in commit_hashes.iter().enumerate() { + let oid = Oid::from_str(hash)?; + let commit_to_apply = repo_guard.find_commit(oid)?; + + info!( + "#[{}/{}] Doing {} - \"{}\"", + index + 1, + commit_hashes.len(), + &hash[0..7], + commit_to_apply.summary().unwrap_or("[No summary]") + ); + + let mut cherrypick_opts = git2::CherrypickOptions::new(); + repo_guard.cherrypick(&commit_to_apply, Some(&mut cherrypick_opts))?; + + let mut repo_index = repo_guard.index()?; + + if repo_index.has_conflicts() { + error!("[CONFLICTED] at commit {}, aborting ...", &hash[0..7]); + let current_head_commit = repo_guard.head()?.peel_to_commit()?; + repo_guard.reset(current_head_commit.as_object(), ResetType::Hard, None)?; + return Err(format!("index_{}/conflicted", index).into()); + } + + let tree_id = repo_index.write_tree()?; + let tree = repo_guard.find_tree(tree_id)?; + + let original_author = commit_to_apply.author(); + + let author = git2::Signature::now( + original_author.name().unwrap_or("Transaction Processor"), + original_author + .email() + .unwrap_or("processor@local.internal"), + )?; + + let current_head = repo_guard.head()?.peel_to_commit()?; + let _new_commit_oid = repo_guard.commit( + Some("HEAD"), + &author, + &author, + &format!( + "Replay transaction: {}", + commit_to_apply.summary().unwrap_or("") + ), + &tree, + &[¤t_head], + )?; + + repo_guard.cleanup_state()?; + info!("applied success!"); + } + + info!("all transaction applied successfully!"); + + Ok(()) +} + // Pull is fetch + merge async fn pull( config: gcm::Configure, @@ -971,10 +1190,6 @@ async fn health_handler(State(_): State) -> impl IntoResponse { pub async fn run(config: gcm::Configure) -> gcm::StandardResult { // REDIS POOL - // - // - // - // let manager = RedisConnectionManager::new(format!( "redis://{}:{}", config.get("REDIS_URI").unwrap_or(&"".to_string()), @@ -987,7 +1202,33 @@ pub async fn run(config: gcm::Configure) -> gcm::StandardResult { .await .expect("failed to build redis pool"); - let (enqueue_tx, enqueue_rx) = channel::(8); + // let connect_str = "host=localhost user=postgres password=my_password dbname=my_database"; + let connect_str = config + .get("POSTGRES_CONN") + .expect("failed to connect postgres"); + info!("connect config: {connect_str}"); + let (mut client, connection) = tokio_postgres::connect(connect_str, NoTls).await?; + tokio::spawn(async move { + if let Err(e) = connection.await { + error!("connection postgres error: {e}"); + } + }); + info!("Creating 'commit' table ..."); + client + .execute( + "CREATE TABLE IF NOT EXISTS recovery_commit ( + id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + commit_hash TEXT NOT NULL, + commit_user TEXT NOT NULL, + email TEXT NOT NULL, + repo TEXT NOT NULL, + ref TEXT NOT NULL, + commit_at TIMESTAMPTZ DEFAULT NOW(), + expire_at TIMESTAMPTZ DEFAULT NOW() + interval '1 hour' + )", + &[], + ) + .await?; let state = AppState { // cached_country_names: common::valid_country_name(), @@ -995,9 +1236,9 @@ pub async fn run(config: gcm::Configure) -> gcm::StandardResult { repo: Arc::new(Mutex::new(Repository::open( config.get("GIT_REPO_LOCAL_DEST").unwrap_or(&"".to_string()), )?)), + postgres: Arc::new(Mutex::new(client)), redis: redis_pool.clone(), fetched: Vec::new(), - enqueue_tx: enqueue_tx.clone(), }; let app = Router::new() @@ -1007,6 +1248,8 @@ pub async fn run(config: gcm::Configure) -> gcm::StandardResult { .route("/push", get(push_handler)) .route("/pull", get(pull_handler)) .route("/health", get(health_handler)) + .route("/replay", post(replay_handler)) + .layer(DefaultBodyLimit::max(100 * 1024 * 1024)) // .route("/healthz", get(reg::health)) .with_state(state); diff --git a/src/main.rs b/src/main.rs index 91de988..c17d195 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,6 +61,7 @@ async fn main() -> gcm::StandardResult { .get("CONFIG_VERSION") .expect("config version not defined") ); + info!("RUNNING: {:?}", config.get("PUBLIC_PORT")); app::run(config.clone()).await?; Ok(())