Server tools to backfill, tail, mirror, and verify PLC logs

mirror: little wrapping proxy server

+508 -72
+340 -56
Cargo.lock
··· 34 "async-compression", 35 "chrono", 36 "clap", 37 - "env_logger", 38 "futures", 39 "log", 40 "reqwest", 41 "reqwest-middleware", 42 "reqwest-retry", ··· 47 "tokio-postgres", 48 "tokio-stream", 49 "tokio-util", 50 ] 51 52 [[package]] ··· 194 ] 195 196 [[package]] 197 name = "bumpalo" 198 version = "3.19.0" 199 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 218 checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" 219 dependencies = [ 220 "find-msvc-tools", 221 "shlex", 222 ] 223 ··· 226 version = "1.0.3" 227 source = "registry+https://github.com/rust-lang/crates.io-index" 228 checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" 229 230 [[package]] 231 name = "chrono" ··· 293 source = "registry+https://github.com/rust-lang/crates.io-index" 294 checksum = "485abf41ac0c8047c07c87c72c8fb3eb5197f6e9d7ded615dfd1a00ae00a0f64" 295 dependencies = [ 296 "compression-core", 297 "flate2", 298 "memchr", 299 ] 300 301 [[package]] ··· 380 ] 381 382 [[package]] 383 - name = "env_filter" 384 - version = "0.1.3" 385 - source = "registry+https://github.com/rust-lang/crates.io-index" 386 - checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" 387 - dependencies = [ 388 - "log", 389 - "regex", 390 - ] 391 - 392 - [[package]] 393 - name = "env_logger" 394 - version = "0.11.8" 395 - source = "registry+https://github.com/rust-lang/crates.io-index" 396 - checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" 397 - dependencies = [ 398 - "anstream", 399 - "anstyle", 400 - "env_filter", 401 - "jiff", 402 - "log", 403 - ] 404 - 405 - [[package]] 406 name = "equivalent" 407 version = "1.0.2" 408 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 632 checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 633 634 [[package]] 635 name = "heck" 636 version = "0.5.0" 637 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 687 checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 688 689 [[package]] 690 name = "hyper" 691 version = "1.7.0" 692 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 700 "http", 701 "http-body", 702 "httparse", 703 "itoa", 704 "pin-project-lite", 705 "pin-utils", ··· 899 900 [[package]] 901 name = "indexmap" 902 - version = "2.11.1" 903 source = "registry+https://github.com/rust-lang/crates.io-index" 904 - checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921" 905 dependencies = [ 906 "equivalent", 907 "hashbrown", ··· 959 checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 960 961 [[package]] 962 - name = "jiff" 963 - version = "0.2.15" 964 - source = "registry+https://github.com/rust-lang/crates.io-index" 965 - checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" 966 - dependencies = [ 967 - "jiff-static", 968 - "log", 969 - "portable-atomic", 970 - "portable-atomic-util", 971 - "serde", 972 - ] 973 - 974 - [[package]] 975 - name = "jiff-static" 976 - version = "0.2.15" 977 source = "registry+https://github.com/rust-lang/crates.io-index" 978 - checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" 979 dependencies = [ 980 - "proc-macro2", 981 - "quote", 982 - "syn", 983 ] 984 985 [[package]] ··· 993 ] 994 995 [[package]] 996 name = "libc" 997 version = "0.2.175" 998 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1036 version = "0.4.28" 1037 source = "registry+https://github.com/rust-lang/crates.io-index" 1038 checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 1039 1040 [[package]] 1041 name = "md-5" ··· 1097 ] 1098 1099 [[package]] 1100 name = "num-traits" 1101 version = "0.2.19" 1102 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1261 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1262 1263 [[package]] 1264 - name = "portable-atomic" 1265 - version = "1.11.1" 1266 source = "registry+https://github.com/rust-lang/crates.io-index" 1267 - checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 1268 1269 [[package]] 1270 - name = "portable-atomic-util" 1271 - version = "0.2.4" 1272 source = "registry+https://github.com/rust-lang/crates.io-index" 1273 - checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" 1274 dependencies = [ 1275 - "portable-atomic", 1276 ] 1277 1278 [[package]] ··· 1323 checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 1324 dependencies = [ 1325 "zerocopy", 1326 ] 1327 1328 [[package]] ··· 1545 ] 1546 1547 [[package]] 1548 name = "ring" 1549 version = "0.17.14" 1550 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1662 1663 [[package]] 1664 name = "serde" 1665 - version = "1.0.219" 1666 source = "registry+https://github.com/rust-lang/crates.io-index" 1667 - checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 1668 dependencies = [ 1669 "serde_derive", 1670 ] 1671 1672 [[package]] 1673 name = "serde_derive" 1674 - version = "1.0.219" 1675 source = "registry+https://github.com/rust-lang/crates.io-index" 1676 - checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 1677 dependencies = [ 1678 "proc-macro2", 1679 "quote", ··· 1705 ] 1706 1707 [[package]] 1708 name = "sha2" 1709 version = "0.10.9" 1710 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1713 "cfg-if", 1714 "cpufeatures", 1715 "digest", 1716 ] 1717 1718 [[package]] ··· 1903 ] 1904 1905 [[package]] 1906 name = "tinystr" 1907 version = "0.8.1" 1908 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2030 ] 2031 2032 [[package]] 2033 name = "tower" 2034 version = "0.5.2" 2035 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2103 checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 2104 dependencies = [ 2105 "once_cell", 2106 ] 2107 2108 [[package]] ··· 2116 version = "1.18.0" 2117 source = "registry+https://github.com/rust-lang/crates.io-index" 2118 checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 2119 2120 [[package]] 2121 name = "unicode-bidi" ··· 2175 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 2176 2177 [[package]] 2178 name = "vcpkg" 2179 version = "0.2.15" 2180 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2345 "wasite", 2346 "web-sys", 2347 ] 2348 2349 [[package]] 2350 name = "winapi" ··· 2610 checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 2611 2612 [[package]] 2613 name = "wit-bindgen" 2614 version = "0.45.1" 2615 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2724 "quote", 2725 "syn", 2726 ]
··· 34 "async-compression", 35 "chrono", 36 "clap", 37 "futures", 38 "log", 39 + "poem", 40 "reqwest", 41 "reqwest-middleware", 42 "reqwest-retry", ··· 47 "tokio-postgres", 48 "tokio-stream", 49 "tokio-util", 50 + "tracing-subscriber", 51 + ] 52 + 53 + [[package]] 54 + name = "alloc-no-stdlib" 55 + version = "2.0.4" 56 + source = "registry+https://github.com/rust-lang/crates.io-index" 57 + checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 58 + 59 + [[package]] 60 + name = "alloc-stdlib" 61 + version = "0.2.2" 62 + source = "registry+https://github.com/rust-lang/crates.io-index" 63 + checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 64 + dependencies = [ 65 + "alloc-no-stdlib", 66 ] 67 68 [[package]] ··· 210 ] 211 212 [[package]] 213 + name = "brotli" 214 + version = "8.0.1" 215 + source = "registry+https://github.com/rust-lang/crates.io-index" 216 + checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" 217 + dependencies = [ 218 + "alloc-no-stdlib", 219 + "alloc-stdlib", 220 + "brotli-decompressor", 221 + ] 222 + 223 + [[package]] 224 + name = "brotli-decompressor" 225 + version = "5.0.0" 226 + source = "registry+https://github.com/rust-lang/crates.io-index" 227 + checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" 228 + dependencies = [ 229 + "alloc-no-stdlib", 230 + "alloc-stdlib", 231 + ] 232 + 233 + [[package]] 234 name = "bumpalo" 235 version = "3.19.0" 236 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 255 checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" 256 dependencies = [ 257 "find-msvc-tools", 258 + "jobserver", 259 + "libc", 260 "shlex", 261 ] 262 ··· 265 version = "1.0.3" 266 source = "registry+https://github.com/rust-lang/crates.io-index" 267 checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" 268 + 269 + [[package]] 270 + name = "cfg_aliases" 271 + version = "0.2.1" 272 + source = "registry+https://github.com/rust-lang/crates.io-index" 273 + checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 274 275 [[package]] 276 name = "chrono" ··· 338 source = "registry+https://github.com/rust-lang/crates.io-index" 339 checksum = "485abf41ac0c8047c07c87c72c8fb3eb5197f6e9d7ded615dfd1a00ae00a0f64" 340 dependencies = [ 341 + "brotli", 342 "compression-core", 343 "flate2", 344 "memchr", 345 + "zstd", 346 + "zstd-safe", 347 ] 348 349 [[package]] ··· 428 ] 429 430 [[package]] 431 name = "equivalent" 432 version = "1.0.2" 433 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 657 checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 658 659 [[package]] 660 + name = "headers" 661 + version = "0.4.1" 662 + source = "registry+https://github.com/rust-lang/crates.io-index" 663 + checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" 664 + dependencies = [ 665 + "base64", 666 + "bytes", 667 + "headers-core", 668 + "http", 669 + "httpdate", 670 + "mime", 671 + "sha1", 672 + ] 673 + 674 + [[package]] 675 + name = "headers-core" 676 + version = "0.3.0" 677 + source = "registry+https://github.com/rust-lang/crates.io-index" 678 + checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" 679 + dependencies = [ 680 + "http", 681 + ] 682 + 683 + [[package]] 684 name = "heck" 685 version = "0.5.0" 686 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 736 checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 737 738 [[package]] 739 + name = "httpdate" 740 + version = "1.0.3" 741 + source = "registry+https://github.com/rust-lang/crates.io-index" 742 + checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 743 + 744 + [[package]] 745 name = "hyper" 746 version = "1.7.0" 747 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 755 "http", 756 "http-body", 757 "httparse", 758 + "httpdate", 759 "itoa", 760 "pin-project-lite", 761 "pin-utils", ··· 955 956 [[package]] 957 name = "indexmap" 958 + version = "2.11.4" 959 source = "registry+https://github.com/rust-lang/crates.io-index" 960 + checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" 961 dependencies = [ 962 "equivalent", 963 "hashbrown", ··· 1015 checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 1016 1017 [[package]] 1018 + name = "jobserver" 1019 + version = "0.1.34" 1020 source = "registry+https://github.com/rust-lang/crates.io-index" 1021 + checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" 1022 dependencies = [ 1023 + "getrandom 0.3.3", 1024 + "libc", 1025 ] 1026 1027 [[package]] ··· 1035 ] 1036 1037 [[package]] 1038 + name = "lazy_static" 1039 + version = "1.5.0" 1040 + source = "registry+https://github.com/rust-lang/crates.io-index" 1041 + checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1042 + 1043 + [[package]] 1044 name = "libc" 1045 version = "0.2.175" 1046 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1084 version = "0.4.28" 1085 source = "registry+https://github.com/rust-lang/crates.io-index" 1086 checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" 1087 + 1088 + [[package]] 1089 + name = "matchers" 1090 + version = "0.2.0" 1091 + source = "registry+https://github.com/rust-lang/crates.io-index" 1092 + checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" 1093 + dependencies = [ 1094 + "regex-automata", 1095 + ] 1096 1097 [[package]] 1098 name = "md-5" ··· 1154 ] 1155 1156 [[package]] 1157 + name = "nix" 1158 + version = "0.30.1" 1159 + source = "registry+https://github.com/rust-lang/crates.io-index" 1160 + checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" 1161 + dependencies = [ 1162 + "bitflags 2.9.4", 1163 + "cfg-if", 1164 + "cfg_aliases", 1165 + "libc", 1166 + ] 1167 + 1168 + [[package]] 1169 + name = "nu-ansi-term" 1170 + version = "0.50.1" 1171 + source = "registry+https://github.com/rust-lang/crates.io-index" 1172 + checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" 1173 + dependencies = [ 1174 + "windows-sys 0.52.0", 1175 + ] 1176 + 1177 + [[package]] 1178 name = "num-traits" 1179 version = "0.2.19" 1180 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1339 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1340 1341 [[package]] 1342 + name = "poem" 1343 + version = "3.1.12" 1344 source = "registry+https://github.com/rust-lang/crates.io-index" 1345 + checksum = "9f977080932c87287147dca052951c3e2696f8759863f6b4e4c0c9ffe7a4cc8b" 1346 + dependencies = [ 1347 + "async-compression", 1348 + "bytes", 1349 + "futures-util", 1350 + "headers", 1351 + "http", 1352 + "http-body-util", 1353 + "hyper", 1354 + "hyper-util", 1355 + "mime", 1356 + "nix", 1357 + "parking_lot 0.12.4", 1358 + "percent-encoding", 1359 + "pin-project-lite", 1360 + "poem-derive", 1361 + "regex", 1362 + "rfc7239", 1363 + "serde", 1364 + "serde_json", 1365 + "serde_urlencoded", 1366 + "smallvec", 1367 + "sync_wrapper", 1368 + "thiserror 2.0.16", 1369 + "tokio", 1370 + "tokio-util", 1371 + "tracing", 1372 + "wildmatch", 1373 + ] 1374 1375 [[package]] 1376 + name = "poem-derive" 1377 + version = "3.1.12" 1378 source = "registry+https://github.com/rust-lang/crates.io-index" 1379 + checksum = "056e2fea6de1cb240ffe23cfc4fc370b629f8be83b5f27e16b7acd5231a72de4" 1380 dependencies = [ 1381 + "proc-macro-crate", 1382 + "proc-macro2", 1383 + "quote", 1384 + "syn", 1385 ] 1386 1387 [[package]] ··· 1432 checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" 1433 dependencies = [ 1434 "zerocopy", 1435 + ] 1436 + 1437 + [[package]] 1438 + name = "proc-macro-crate" 1439 + version = "3.4.0" 1440 + source = "registry+https://github.com/rust-lang/crates.io-index" 1441 + checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" 1442 + dependencies = [ 1443 + "toml_edit", 1444 ] 1445 1446 [[package]] ··· 1663 ] 1664 1665 [[package]] 1666 + name = "rfc7239" 1667 + version = "0.1.3" 1668 + source = "registry+https://github.com/rust-lang/crates.io-index" 1669 + checksum = "4a82f1d1e38e9a85bb58ffcfadf22ed6f2c94e8cd8581ec2b0f80a2a6858350f" 1670 + dependencies = [ 1671 + "uncased", 1672 + ] 1673 + 1674 + [[package]] 1675 name = "ring" 1676 version = "0.17.14" 1677 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1789 1790 [[package]] 1791 name = "serde" 1792 + version = "1.0.226" 1793 + source = "registry+https://github.com/rust-lang/crates.io-index" 1794 + checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" 1795 + dependencies = [ 1796 + "serde_core", 1797 + "serde_derive", 1798 + ] 1799 + 1800 + [[package]] 1801 + name = "serde_core" 1802 + version = "1.0.226" 1803 source = "registry+https://github.com/rust-lang/crates.io-index" 1804 + checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" 1805 dependencies = [ 1806 "serde_derive", 1807 ] 1808 1809 [[package]] 1810 name = "serde_derive" 1811 + version = "1.0.226" 1812 source = "registry+https://github.com/rust-lang/crates.io-index" 1813 + checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" 1814 dependencies = [ 1815 "proc-macro2", 1816 "quote", ··· 1842 ] 1843 1844 [[package]] 1845 + name = "sha1" 1846 + version = "0.10.6" 1847 + source = "registry+https://github.com/rust-lang/crates.io-index" 1848 + checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1849 + dependencies = [ 1850 + "cfg-if", 1851 + "cpufeatures", 1852 + "digest", 1853 + ] 1854 + 1855 + [[package]] 1856 name = "sha2" 1857 version = "0.10.9" 1858 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1861 "cfg-if", 1862 "cpufeatures", 1863 "digest", 1864 + ] 1865 + 1866 + [[package]] 1867 + name = "sharded-slab" 1868 + version = "0.1.7" 1869 + source = "registry+https://github.com/rust-lang/crates.io-index" 1870 + checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1871 + dependencies = [ 1872 + "lazy_static", 1873 ] 1874 1875 [[package]] ··· 2060 ] 2061 2062 [[package]] 2063 + name = "thread_local" 2064 + version = "1.1.9" 2065 + source = "registry+https://github.com/rust-lang/crates.io-index" 2066 + checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" 2067 + dependencies = [ 2068 + "cfg-if", 2069 + ] 2070 + 2071 + [[package]] 2072 name = "tinystr" 2073 version = "0.8.1" 2074 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2196 ] 2197 2198 [[package]] 2199 + name = "toml_datetime" 2200 + version = "0.7.2" 2201 + source = "registry+https://github.com/rust-lang/crates.io-index" 2202 + checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" 2203 + dependencies = [ 2204 + "serde_core", 2205 + ] 2206 + 2207 + [[package]] 2208 + name = "toml_edit" 2209 + version = "0.23.6" 2210 + source = "registry+https://github.com/rust-lang/crates.io-index" 2211 + checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" 2212 + dependencies = [ 2213 + "indexmap", 2214 + "toml_datetime", 2215 + "toml_parser", 2216 + "winnow", 2217 + ] 2218 + 2219 + [[package]] 2220 + name = "toml_parser" 2221 + version = "1.0.3" 2222 + source = "registry+https://github.com/rust-lang/crates.io-index" 2223 + checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" 2224 + dependencies = [ 2225 + "winnow", 2226 + ] 2227 + 2228 + [[package]] 2229 name = "tower" 2230 version = "0.5.2" 2231 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2299 checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" 2300 dependencies = [ 2301 "once_cell", 2302 + "valuable", 2303 + ] 2304 + 2305 + [[package]] 2306 + name = "tracing-log" 2307 + version = "0.2.0" 2308 + source = "registry+https://github.com/rust-lang/crates.io-index" 2309 + checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 2310 + dependencies = [ 2311 + "log", 2312 + "once_cell", 2313 + "tracing-core", 2314 + ] 2315 + 2316 + [[package]] 2317 + name = "tracing-subscriber" 2318 + version = "0.3.20" 2319 + source = "registry+https://github.com/rust-lang/crates.io-index" 2320 + checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" 2321 + dependencies = [ 2322 + "matchers", 2323 + "nu-ansi-term", 2324 + "once_cell", 2325 + "regex-automata", 2326 + "sharded-slab", 2327 + "smallvec", 2328 + "thread_local", 2329 + "tracing", 2330 + "tracing-core", 2331 + "tracing-log", 2332 ] 2333 2334 [[package]] ··· 2342 version = "1.18.0" 2343 source = "registry+https://github.com/rust-lang/crates.io-index" 2344 checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" 2345 + 2346 + [[package]] 2347 + name = "uncased" 2348 + version = "0.9.10" 2349 + source = "registry+https://github.com/rust-lang/crates.io-index" 2350 + checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697" 2351 + dependencies = [ 2352 + "version_check", 2353 + ] 2354 2355 [[package]] 2356 name = "unicode-bidi" ··· 2410 checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 2411 2412 [[package]] 2413 + name = "valuable" 2414 + version = "0.1.1" 2415 + source = "registry+https://github.com/rust-lang/crates.io-index" 2416 + checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 2417 + 2418 + [[package]] 2419 name = "vcpkg" 2420 version = "0.2.15" 2421 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2586 "wasite", 2587 "web-sys", 2588 ] 2589 + 2590 + [[package]] 2591 + name = "wildmatch" 2592 + version = "2.5.0" 2593 + source = "registry+https://github.com/rust-lang/crates.io-index" 2594 + checksum = "39b7d07a236abaef6607536ccfaf19b396dbe3f5110ddb73d39f4562902ed382" 2595 2596 [[package]] 2597 name = "winapi" ··· 2857 checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 2858 2859 [[package]] 2860 + name = "winnow" 2861 + version = "0.7.13" 2862 + source = "registry+https://github.com/rust-lang/crates.io-index" 2863 + checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" 2864 + dependencies = [ 2865 + "memchr", 2866 + ] 2867 + 2868 + [[package]] 2869 name = "wit-bindgen" 2870 version = "0.45.1" 2871 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2980 "quote", 2981 "syn", 2982 ] 2983 + 2984 + [[package]] 2985 + name = "zstd" 2986 + version = "0.13.3" 2987 + source = "registry+https://github.com/rust-lang/crates.io-index" 2988 + checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" 2989 + dependencies = [ 2990 + "zstd-safe", 2991 + ] 2992 + 2993 + [[package]] 2994 + name = "zstd-safe" 2995 + version = "7.2.4" 2996 + source = "registry+https://github.com/rust-lang/crates.io-index" 2997 + checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" 2998 + dependencies = [ 2999 + "zstd-sys", 3000 + ] 3001 + 3002 + [[package]] 3003 + name = "zstd-sys" 3004 + version = "2.0.16+zstd.1.5.7" 3005 + source = "registry+https://github.com/rust-lang/crates.io-index" 3006 + checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" 3007 + dependencies = [ 3008 + "cc", 3009 + "pkg-config", 3010 + ]
+3 -2
Cargo.toml
··· 1 [package] 2 name = "allegedly" 3 - description = "public ledger tools and services (for the PLC)" 4 license = "MIT OR Apache-2.0" 5 version = "0.1.0" 6 edition = "2024" ··· 11 async-compression = { version = "0.4.30", features = ["futures-io", "tokio", "gzip"] } 12 chrono = { version = "0.4.42", features = ["serde"] } 13 clap = { version = "4.5.47", features = ["derive", "env"] } 14 - env_logger = "0.11.8" 15 futures = "0.3.31" 16 log = "0.4.28" 17 reqwest = { version = "0.12.23", features = ["stream"] } 18 reqwest-middleware = "0.4.2" 19 reqwest-retry = "0.7.0" ··· 24 tokio-postgres = { version = "0.7.13", features = ["with-chrono-0_4", "with-serde_json-1"] } 25 tokio-stream = { version = "0.1.17", features = ["io-util"] } 26 tokio-util = { version = "0.7.16", features = ["compat"] }
··· 1 [package] 2 name = "allegedly" 3 + description = "public ledger server tools and services (for the PLC)" 4 license = "MIT OR Apache-2.0" 5 version = "0.1.0" 6 edition = "2024" ··· 11 async-compression = { version = "0.4.30", features = ["futures-io", "tokio", "gzip"] } 12 chrono = { version = "0.4.42", features = ["serde"] } 13 clap = { version = "4.5.47", features = ["derive", "env"] } 14 futures = "0.3.31" 15 log = "0.4.28" 16 + poem = { version = "3.1.12", features = ["compression"] } 17 reqwest = { version = "0.12.23", features = ["stream"] } 18 reqwest-middleware = "0.4.2" 19 reqwest-retry = "0.7.0" ··· 24 tokio-postgres = { version = "0.7.13", features = ["with-chrono-0_4", "with-serde_json-1"] } 25 tokio-stream = { version = "0.1.17", features = ["io-util"] } 26 tokio-util = { version = "0.7.16", features = ["compat"] } 27 + tracing-subscriber = { version = "0.3.20", features = ["env-filter"] }
+9 -1
readme.md
··· 1 # Allegedly 2 3 - Some [public ledger](https://github.com/did-method-plc/did-method-plc) tools and services 4 5 Allegedly can 6 7 - Tail PLC ops to stdout: `allegedly tail | jq` 8 - Export PLC ops to weekly gzipped bundles: `allegdly bundle --dest ./some-folder` 9 - Dump bundled ops to stdout FAST: `allegedly backfill --source-workers 6 | pv -l > /ops-unordered.jsonl` 10 11 (add `--help` to any command for more info about it) 12
··· 1 # Allegedly 2 3 + Some [public ledger](https://github.com/did-method-plc/did-method-plc) server tools and services 4 5 Allegedly can 6 7 - Tail PLC ops to stdout: `allegedly tail | jq` 8 - Export PLC ops to weekly gzipped bundles: `allegdly bundle --dest ./some-folder` 9 - Dump bundled ops to stdout FAST: `allegedly backfill --source-workers 6 | pv -l > /ops-unordered.jsonl` 10 + - Wrap the reference PLC server and run it as a mirror: 11 + 12 + ```bash 13 + allegedly mirror \ 14 + --bind 0.0.0.0:8000 \ 15 + --wrap http://127.0.0.1:3000 \ 16 + --wrap-pg "postgresql://postgres:postgres@localhost:5432/postgres" 17 + ``` 18 19 (add `--help` to any command for more info about it) 20
+50 -5
src/bin/allegedly.rs
··· 1 use allegedly::{ 2 Db, Dt, ExportPage, FolderSource, HttpSource, PageBoundaryState, backfill, backfill_to_pg, 3 - bin_init, pages_to_pg, pages_to_weeks, poll_upstream, 4 }; 5 - use clap::{Parser, Subcommand}; 6 use reqwest::Url; 7 - use std::{path::PathBuf, time::Instant}; 8 use tokio::sync::{mpsc, oneshot}; 9 10 #[derive(Debug, Parser)] ··· 71 #[arg(long, action)] 72 clobber: bool, 73 }, 74 /// Poll an upstream PLC server and log new ops to stdout 75 Tail { 76 /// Begin tailing from a specific timestamp for replay or wait-until ··· 121 122 #[tokio::main] 123 async fn main() { 124 - bin_init("main"); 125 - 126 let args = Cli::parse(); 127 128 let t0 = Instant::now(); 129 match args.command { ··· 205 log::trace!("ensuring output directory exists"); 206 std::fs::create_dir_all(&dest).unwrap(); 207 pages_to_weeks(rx, dest, clobber).await.unwrap(); 208 } 209 Commands::Tail { after } => { 210 let mut url = args.upstream;
··· 1 use allegedly::{ 2 Db, Dt, ExportPage, FolderSource, HttpSource, PageBoundaryState, backfill, backfill_to_pg, 3 + bin_init, pages_to_pg, pages_to_weeks, poll_upstream, serve, 4 }; 5 + use clap::{CommandFactory, Parser, Subcommand}; 6 use reqwest::Url; 7 + use std::{net::SocketAddr, path::PathBuf, time::Instant}; 8 use tokio::sync::{mpsc, oneshot}; 9 10 #[derive(Debug, Parser)] ··· 71 #[arg(long, action)] 72 clobber: bool, 73 }, 74 + /// Wrap a did-method-plc server, syncing upstream and blocking op submits 75 + Mirror { 76 + /// the wrapped did-method-plc server 77 + #[arg(long, env)] 78 + wrap: Url, 79 + /// the wrapped did-method-plc server's database (write access required) 80 + #[arg(long, env)] 81 + wrap_pg: Url, 82 + /// wrapping server listen address 83 + #[arg(short, long, env)] 84 + #[clap(default_value = "127.0.0.1:8000")] 85 + bind: SocketAddr, 86 + }, 87 /// Poll an upstream PLC server and log new ops to stdout 88 Tail { 89 /// Begin tailing from a specific timestamp for replay or wait-until ··· 134 135 #[tokio::main] 136 async fn main() { 137 let args = Cli::parse(); 138 + let matches = Cli::command().get_matches(); 139 + let name = matches.subcommand().map(|(name, _)| name).unwrap_or("???"); 140 + bin_init(name); 141 142 let t0 = Instant::now(); 143 match args.command { ··· 219 log::trace!("ensuring output directory exists"); 220 std::fs::create_dir_all(&dest).unwrap(); 221 pages_to_weeks(rx, dest, clobber).await.unwrap(); 222 + } 223 + Commands::Mirror { 224 + wrap, 225 + wrap_pg, 226 + bind, 227 + } => { 228 + let db = Db::new(wrap_pg.as_str()).await.unwrap(); 229 + let latest = db 230 + .get_latest() 231 + .await 232 + .unwrap() 233 + .expect("there to be at least one op in the db. did you backfill?"); 234 + 235 + let (tx, rx) = mpsc::channel(2); 236 + // upstream poller 237 + tokio::task::spawn(async move { 238 + log::info!("starting poll reader..."); 239 + let mut url = args.upstream; 240 + url.set_path("/export"); 241 + tokio::task::spawn( 242 + async move { poll_upstream(Some(latest), url, tx).await.unwrap() }, 243 + ); 244 + }); 245 + // db writer 246 + let poll_db = db.clone(); 247 + tokio::task::spawn(async move { 248 + log::info!("starting db writer..."); 249 + pages_to_pg(poll_db, rx).await.unwrap(); 250 + }); 251 + 252 + serve(wrap, bind).await.unwrap(); 253 } 254 Commands::Tail { after } => { 255 let mut url = args.upstream;
+19 -7
src/lib.rs
··· 2 3 mod backfill; 4 mod client; 5 mod plc_pg; 6 mod poll; 7 mod weekly; 8 9 pub use backfill::backfill; 10 pub use client::CLIENT; 11 pub use plc_pg::{Db, backfill_to_pg, pages_to_pg}; 12 pub use poll::{PageBoundaryState, get_page, poll_upstream}; 13 pub use weekly::{BundleSource, FolderSource, HttpSource, Week, pages_to_weeks, week_to_pages}; ··· 58 } 59 } 60 61 - pub fn bin_init(name: &str) { 62 - use env_logger::{Builder, Env}; 63 - Builder::from_env(Env::new().filter_or("RUST_LOG", "info")).init(); 64 - 65 - log::info!( 66 r" 67 68 \ | | | | ··· 70 _/ _\ _| _| \___| \__, | \___| \__,_| _| \_, | (v{}) 71 ____| __/ 72 ", 73 - env!("CARGO_PKG_VERSION") 74 - ); 75 }
··· 2 3 mod backfill; 4 mod client; 5 + mod mirror; 6 mod plc_pg; 7 mod poll; 8 mod weekly; 9 10 pub use backfill::backfill; 11 pub use client::CLIENT; 12 + pub use mirror::serve; 13 pub use plc_pg::{Db, backfill_to_pg, pages_to_pg}; 14 pub use poll::{PageBoundaryState, get_page, poll_upstream}; 15 pub use weekly::{BundleSource, FolderSource, HttpSource, Week, pages_to_weeks, week_to_pages}; ··· 60 } 61 } 62 63 + pub fn logo(name: &str) -> String { 64 + format!( 65 r" 66 67 \ | | | | ··· 69 _/ _\ _| _| \___| \__, | \___| \__,_| _| \_, | (v{}) 70 ____| __/ 71 ", 72 + env!("CARGO_PKG_VERSION"), 73 + ) 74 + } 75 + 76 + pub fn bin_init(name: &str) { 77 + if std::env::var_os("RUST_LOG").is_none() { 78 + unsafe { std::env::set_var("RUST_LOG", "info") }; 79 + } 80 + let filter = tracing_subscriber::EnvFilter::from_default_env(); 81 + tracing_subscriber::fmt() 82 + .with_writer(std::io::stderr) 83 + .with_env_filter(filter) 84 + .init(); 85 + 86 + log::info!("{}", logo(name)); 87 }
+70
src/mirror.rs
···
··· 1 + use crate::logo; 2 + use poem::{ 3 + EndpointExt, Error, IntoResponse, Request, Response, Result, Route, Server, get, handler, 4 + http::{StatusCode, Uri}, 5 + listener::TcpListener, 6 + middleware::{AddData, CatchPanic, Compression, Cors, Tracing}, 7 + web::Data, 8 + }; 9 + use reqwest::{Client, Url}; 10 + use std::net::SocketAddr; 11 + use std::time::Duration; 12 + 13 + #[derive(Debug, Clone)] 14 + struct State { 15 + client: Client, 16 + plc: Url, 17 + } 18 + 19 + #[handler] 20 + fn hello() -> String { 21 + logo("mirror") 22 + } 23 + 24 + #[handler] 25 + async fn proxy(req: &Request, Data(state): Data<&State>) -> Result<impl IntoResponse> { 26 + let mut target = state.plc.clone(); 27 + target.set_path(req.uri().path()); 28 + let upstream_res = state 29 + .client 30 + .get(target) 31 + .headers(req.headers().clone()) 32 + .send() 33 + .await 34 + .map_err(|e| { 35 + log::error!("upstream req fail: {e}"); 36 + Error::from_string("request to plc server failed", StatusCode::BAD_GATEWAY) 37 + })?; 38 + let mut res = Response::default(); 39 + upstream_res.headers().iter().for_each(|(k, v)| { 40 + res.headers_mut().insert(k, v.to_owned()); 41 + }); 42 + res.set_status(upstream_res.status()); 43 + res.set_version(upstream_res.version()); 44 + res.set_body(upstream_res.bytes().await.unwrap()); 45 + Ok(res) 46 + } 47 + 48 + #[handler] 49 + async fn nope(uri: &Uri) -> Result<impl IntoResponse> { 50 + log::info!("ha nope, {uri:?}"); 51 + Ok(()) 52 + } 53 + 54 + pub async fn serve(plc: Url, bind: SocketAddr) -> std::io::Result<()> { 55 + let client = Client::builder() 56 + .timeout(Duration::from_secs(3)) 57 + .build() 58 + .unwrap(); 59 + let state = State { client, plc }; 60 + 61 + let app = Route::new() 62 + .at("/", get(hello)) 63 + .at("/:any", get(proxy).post(nope)) 64 + .with(AddData::new(state)) 65 + .with(Cors::new().allow_credentials(false)) 66 + .with(Compression::new()) 67 + .with(CatchPanic::new()) 68 + .with(Tracing); 69 + Server::new(TcpListener::bind(bind)).run(app).await 70 + }
+17 -1
src/plc_pg.rs
··· 70 71 Ok(client) 72 } 73 } 74 75 pub async fn pages_to_pg(db: Db, mut pages: mpsc::Receiver<ExportPage>) -> Result<(), PgError> { ··· 78 let ops_stmt = client 79 .prepare( 80 r#"INSERT INTO operations (did, operation, cid, nullified, "createdAt") 81 - VALUES ($1, $2, $3, $4, $5)"#, 82 ) 83 .await?; 84 let did_stmt = client
··· 70 71 Ok(client) 72 } 73 + 74 + pub async fn get_latest(&self) -> Result<Option<Dt>, PgError> { 75 + let client = self.connect().await?; 76 + let dt: Option<Dt> = client 77 + .query_opt( 78 + r#"SELECT "createdAt" 79 + FROM operations 80 + ORDER BY "createdAt" DESC 81 + LIMIT 1"#, 82 + &[], 83 + ) 84 + .await? 85 + .map(|row| row.get(0)); 86 + Ok(dt) 87 + } 88 } 89 90 pub async fn pages_to_pg(db: Db, mut pages: mpsc::Receiver<ExportPage>) -> Result<(), PgError> { ··· 93 let ops_stmt = client 94 .prepare( 95 r#"INSERT INTO operations (did, operation, cid, nullified, "createdAt") 96 + VALUES ($1, $2, $3, $4, $5) 97 + ON CONFLICT do nothing"#, 98 ) 99 .await?; 100 let did_stmt = client