More structure, nice oauth flow on the CLI (localhost-only, but honestly fine)

Probably going to redo
the appview stuff entirely

+3025 -115
+5
.zed/settings.json
··· 12 12 } 13 13 } 14 14 } 15 + }, 16 + "rust-analyzer": { 17 + "binary": { 18 + "path_lookup": true 19 + } 15 20 } 16 21 }
+1467 -24
Cargo.lock
··· 18 18 checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 19 20 20 [[package]] 21 + name = "adler32" 22 + version = "1.2.0" 23 + source = "registry+https://github.com/rust-lang/crates.io-index" 24 + checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" 25 + 26 + [[package]] 21 27 name = "ahash" 22 28 version = "0.8.12" 23 29 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 36 42 checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 37 43 dependencies = [ 38 44 "memchr", 45 + ] 46 + 47 + [[package]] 48 + name = "alloc-no-stdlib" 49 + version = "2.0.4" 50 + source = "registry+https://github.com/rust-lang/crates.io-index" 51 + checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 52 + 53 + [[package]] 54 + name = "alloc-stdlib" 55 + version = "0.2.2" 56 + source = "registry+https://github.com/rust-lang/crates.io-index" 57 + checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 58 + dependencies = [ 59 + "alloc-no-stdlib", 39 60 ] 40 61 41 62 [[package]] ··· 116 137 checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" 117 138 118 139 [[package]] 140 + name = "ascii" 141 + version = "1.1.0" 142 + source = "registry+https://github.com/rust-lang/crates.io-index" 143 + checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" 144 + 145 + [[package]] 119 146 name = "async-compression" 120 147 version = "0.4.23" 121 148 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 149 176 "quote", 150 177 "syn", 151 178 ] 179 + 180 + [[package]] 181 + name = "atomic-waker" 182 + version = "1.1.2" 183 + source = "registry+https://github.com/rust-lang/crates.io-index" 184 + checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 152 185 153 186 [[package]] 154 187 name = "atrium-api" ··· 230 263 "jose-jwa", 231 264 "jose-jwk", 232 265 "p256", 233 - "rand", 266 + "rand 0.8.5", 234 267 "reqwest", 235 268 "serde", 236 269 "serde_html_form", ··· 435 468 checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" 436 469 437 470 [[package]] 471 + name = "bincode" 472 + version = "1.3.3" 473 + source = "registry+https://github.com/rust-lang/crates.io-index" 474 + checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 475 + dependencies = [ 476 + "serde", 477 + ] 478 + 479 + [[package]] 480 + name = "bitflags" 481 + version = "1.3.2" 482 + source = "registry+https://github.com/rust-lang/crates.io-index" 483 + checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 484 + 485 + [[package]] 438 486 name = "bitflags" 439 487 version = "2.9.0" 440 488 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 450 498 ] 451 499 452 500 [[package]] 501 + name = "borrow-or-share" 502 + version = "0.2.2" 503 + source = "registry+https://github.com/rust-lang/crates.io-index" 504 + checksum = "3eeab4423108c5d7c744f4d234de88d18d636100093ae04caf4825134b9c3a32" 505 + 506 + [[package]] 507 + name = "brotli" 508 + version = "3.5.0" 509 + source = "registry+https://github.com/rust-lang/crates.io-index" 510 + checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" 511 + dependencies = [ 512 + "alloc-no-stdlib", 513 + "alloc-stdlib", 514 + "brotli-decompressor", 515 + ] 516 + 517 + [[package]] 518 + name = "brotli-decompressor" 519 + version = "2.5.1" 520 + source = "registry+https://github.com/rust-lang/crates.io-index" 521 + checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" 522 + dependencies = [ 523 + "alloc-no-stdlib", 524 + "alloc-stdlib", 525 + ] 526 + 527 + [[package]] 528 + name = "buf_redux" 529 + version = "0.8.4" 530 + source = "registry+https://github.com/rust-lang/crates.io-index" 531 + checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" 532 + dependencies = [ 533 + "memchr", 534 + "safemem", 535 + ] 536 + 537 + [[package]] 453 538 name = "bumpalo" 454 539 version = "3.17.0" 455 540 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 528 613 ] 529 614 530 615 [[package]] 616 + name = "chunked_transfer" 617 + version = "1.5.0" 618 + source = "registry+https://github.com/rust-lang/crates.io-index" 619 + checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" 620 + 621 + [[package]] 531 622 name = "cid" 532 623 version = "0.11.1" 533 624 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 610 701 "itoa", 611 702 "rustversion", 612 703 "ryu", 704 + "serde", 613 705 "static_assertions", 614 706 ] 615 707 616 708 [[package]] 709 + name = "compact_string" 710 + version = "0.1.0" 711 + source = "registry+https://github.com/rust-lang/crates.io-index" 712 + checksum = "5255b88d8ea09573f588088dea17fbea682b4442abea6761a15d1da2c3a76c5c" 713 + dependencies = [ 714 + "serde", 715 + "thiserror 1.0.69", 716 + ] 717 + 718 + [[package]] 617 719 name = "concurrent-queue" 618 720 version = "2.5.0" 619 721 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 723 825 checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" 724 826 dependencies = [ 725 827 "generic-array", 726 - "rand_core", 828 + "rand_core 0.6.4", 727 829 "subtle", 728 830 "zeroize", 729 831 ] ··· 739 841 ] 740 842 741 843 [[package]] 844 + name = "curve25519-dalek" 845 + version = "4.1.3" 846 + source = "registry+https://github.com/rust-lang/crates.io-index" 847 + checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" 848 + dependencies = [ 849 + "cfg-if", 850 + "cpufeatures", 851 + "curve25519-dalek-derive", 852 + "digest", 853 + "fiat-crypto", 854 + "rustc_version", 855 + "subtle", 856 + "zeroize", 857 + ] 858 + 859 + [[package]] 860 + name = "curve25519-dalek-derive" 861 + version = "0.1.1" 862 + source = "registry+https://github.com/rust-lang/crates.io-index" 863 + checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" 864 + dependencies = [ 865 + "proc-macro2", 866 + "quote", 867 + "syn", 868 + ] 869 + 870 + [[package]] 742 871 name = "darling" 743 872 version = "0.20.11" 744 873 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 814 943 ] 815 944 816 945 [[package]] 946 + name = "deadpool" 947 + version = "0.12.2" 948 + source = "registry+https://github.com/rust-lang/crates.io-index" 949 + checksum = "5ed5957ff93768adf7a65ab167a17835c3d2c3c50d084fe305174c112f468e2f" 950 + dependencies = [ 951 + "deadpool-runtime", 952 + "num_cpus", 953 + "tokio", 954 + ] 955 + 956 + [[package]] 957 + name = "deadpool-runtime" 958 + version = "0.1.4" 959 + source = "registry+https://github.com/rust-lang/crates.io-index" 960 + checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" 961 + 962 + [[package]] 963 + name = "deflate" 964 + version = "1.0.0" 965 + source = "registry+https://github.com/rust-lang/crates.io-index" 966 + checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" 967 + dependencies = [ 968 + "adler32", 969 + "gzip-header", 970 + ] 971 + 972 + [[package]] 817 973 name = "der" 818 974 version = "0.7.10" 819 975 source = "registry+https://github.com/rust-lang/crates.io-index" 820 976 checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" 821 977 dependencies = [ 822 978 "const-oid", 979 + "pem-rfc7468", 823 980 "zeroize", 824 981 ] 825 982 ··· 866 1023 source = "registry+https://github.com/rust-lang/crates.io-index" 867 1024 checksum = "ff3e1edb1f37b4953dd5176916347289ed43d7119cc2e6c7c3f7849ff44ea506" 868 1025 dependencies = [ 1026 + "bitflags 2.9.0", 1027 + "byteorder", 1028 + "chrono", 869 1029 "diesel_derives", 1030 + "itoa", 870 1031 "libsqlite3-sys", 1032 + "pq-sys", 1033 + "serde_json", 871 1034 "time", 872 1035 ] 873 1036 874 1037 [[package]] 1038 + name = "diesel-async" 1039 + version = "0.5.2" 1040 + source = "registry+https://github.com/rust-lang/crates.io-index" 1041 + checksum = "51a307ac00f7c23f526a04a77761a0519b9f0eb2838ebf5b905a58580095bdcb" 1042 + dependencies = [ 1043 + "async-trait", 1044 + "deadpool", 1045 + "diesel", 1046 + "futures-util", 1047 + "scoped-futures", 1048 + "tokio", 1049 + "tokio-postgres", 1050 + ] 1051 + 1052 + [[package]] 875 1053 name = "diesel_derives" 876 1054 version = "2.2.5" 877 1055 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 885 1063 ] 886 1064 887 1065 [[package]] 1066 + name = "diesel_migrations" 1067 + version = "2.2.0" 1068 + source = "registry+https://github.com/rust-lang/crates.io-index" 1069 + checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6" 1070 + dependencies = [ 1071 + "diesel", 1072 + "migrations_internals", 1073 + "migrations_macros", 1074 + ] 1075 + 1076 + [[package]] 888 1077 name = "diesel_table_macro_syntax" 889 1078 version = "0.2.0" 890 1079 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 947 1136 "elliptic-curve", 948 1137 "rfc6979", 949 1138 "signature", 1139 + "spki", 1140 + ] 1141 + 1142 + [[package]] 1143 + name = "ed25519" 1144 + version = "2.2.3" 1145 + source = "registry+https://github.com/rust-lang/crates.io-index" 1146 + checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" 1147 + dependencies = [ 1148 + "signature", 1149 + ] 1150 + 1151 + [[package]] 1152 + name = "ed25519-dalek" 1153 + version = "2.1.1" 1154 + source = "registry+https://github.com/rust-lang/crates.io-index" 1155 + checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" 1156 + dependencies = [ 1157 + "curve25519-dalek", 1158 + "ed25519", 1159 + "rand_core 0.6.4", 1160 + "sha2", 1161 + "subtle", 1162 + "zeroize", 950 1163 ] 951 1164 952 1165 [[package]] ··· 962 1175 checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" 963 1176 dependencies = [ 964 1177 "base16ct", 1178 + "base64ct", 965 1179 "crypto-bigint", 966 1180 "digest", 967 1181 "ff", 968 1182 "generic-array", 969 1183 "group", 970 - "rand_core", 1184 + "pem-rfc7468", 1185 + "pkcs8", 1186 + "rand_core 0.6.4", 971 1187 "sec1", 1188 + "serde_json", 1189 + "serdect", 972 1190 "subtle", 973 1191 "zeroize", 1192 + ] 1193 + 1194 + [[package]] 1195 + name = "encoding_rs" 1196 + version = "0.8.35" 1197 + source = "registry+https://github.com/rust-lang/crates.io-index" 1198 + checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 1199 + dependencies = [ 1200 + "cfg-if", 974 1201 ] 975 1202 976 1203 [[package]] ··· 1039 1266 ] 1040 1267 1041 1268 [[package]] 1269 + name = "facet" 1270 + version = "0.9.8" 1271 + source = "registry+https://github.com/rust-lang/crates.io-index" 1272 + checksum = "36b55e98a6cc63c843db91f6db4be9f47067b9bd07d93560da0ba4c0f55f20fd" 1273 + dependencies = [ 1274 + "facet-core", 1275 + "facet-derive", 1276 + ] 1277 + 1278 + [[package]] 1279 + name = "facet-core" 1280 + version = "0.12.0" 1281 + source = "registry+https://github.com/rust-lang/crates.io-index" 1282 + checksum = "5227e1c925a1f21ea3ebff90e08af0b34b59975171cf00fcee36b917941d1ac0" 1283 + dependencies = [ 1284 + "bitflags 2.9.0", 1285 + "camino", 1286 + "impls", 1287 + ] 1288 + 1289 + [[package]] 1290 + name = "facet-derive" 1291 + version = "0.9.8" 1292 + source = "registry+https://github.com/rust-lang/crates.io-index" 1293 + checksum = "9982f286c2121f36d6921c7ecedde49158a5ba8eef622a4fd777ff502c1348e3" 1294 + dependencies = [ 1295 + "facet-core", 1296 + "facet-derive-emit", 1297 + ] 1298 + 1299 + [[package]] 1300 + name = "facet-derive-emit" 1301 + version = "0.15.0" 1302 + source = "registry+https://github.com/rust-lang/crates.io-index" 1303 + checksum = "2ed20e29f8aa502cf1263eb040e3442ef84636e89a88ee8dffcda336a1152a38" 1304 + dependencies = [ 1305 + "facet-derive-parse", 1306 + ] 1307 + 1308 + [[package]] 1309 + name = "facet-derive-parse" 1310 + version = "0.15.0" 1311 + source = "registry+https://github.com/rust-lang/crates.io-index" 1312 + checksum = "71ea122ff95ac65c86bf18d0ba7f04f087ef5dd20f7dc76cb5bd69cbb14bd8ad" 1313 + dependencies = [ 1314 + "unsynn", 1315 + ] 1316 + 1317 + [[package]] 1318 + name = "fallible-iterator" 1319 + version = "0.2.0" 1320 + source = "registry+https://github.com/rust-lang/crates.io-index" 1321 + checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" 1322 + 1323 + [[package]] 1042 1324 name = "fallible-iterator" 1043 1325 version = "0.3.0" 1044 1326 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1062 1344 source = "registry+https://github.com/rust-lang/crates.io-index" 1063 1345 checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" 1064 1346 dependencies = [ 1065 - "rand_core", 1347 + "rand_core 0.6.4", 1066 1348 "subtle", 1067 1349 ] 1068 1350 1069 1351 [[package]] 1352 + name = "fiat-crypto" 1353 + version = "0.2.9" 1354 + source = "registry+https://github.com/rust-lang/crates.io-index" 1355 + checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" 1356 + 1357 + [[package]] 1358 + name = "filetime" 1359 + version = "0.2.25" 1360 + source = "registry+https://github.com/rust-lang/crates.io-index" 1361 + checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" 1362 + dependencies = [ 1363 + "cfg-if", 1364 + "libc", 1365 + "libredox", 1366 + "windows-sys 0.59.0", 1367 + ] 1368 + 1369 + [[package]] 1070 1370 name = "flate2" 1071 1371 version = "1.1.1" 1072 1372 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1077 1377 ] 1078 1378 1079 1379 [[package]] 1380 + name = "fluent-uri" 1381 + version = "0.3.2" 1382 + source = "registry+https://github.com/rust-lang/crates.io-index" 1383 + checksum = "1918b65d96df47d3591bed19c5cca17e3fa5d0707318e4b5ef2eae01764df7e5" 1384 + dependencies = [ 1385 + "borrow-or-share", 1386 + "ref-cast", 1387 + ] 1388 + 1389 + [[package]] 1080 1390 name = "fnv" 1081 1391 version = "1.0.7" 1082 1392 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1122 1432 "diatomic-waker", 1123 1433 "futures-core", 1124 1434 "pin-project-lite", 1125 - "spin", 1435 + "spin 0.9.8", 1126 1436 ] 1127 1437 1128 1438 [[package]] ··· 1132 1442 checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 1133 1443 dependencies = [ 1134 1444 "futures-core", 1445 + "futures-sink", 1135 1446 ] 1136 1447 1137 1448 [[package]] ··· 1216 1527 source = "registry+https://github.com/rust-lang/crates.io-index" 1217 1528 checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 1218 1529 dependencies = [ 1530 + "serde", 1219 1531 "typenum", 1220 1532 "version_check", 1221 1533 "zeroize", ··· 1237 1549 checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" 1238 1550 dependencies = [ 1239 1551 "cfg-if", 1552 + "js-sys", 1240 1553 "libc", 1241 1554 "wasi 0.11.0+wasi-snapshot-preview1", 1555 + "wasm-bindgen", 1242 1556 ] 1243 1557 1244 1558 [[package]] ··· 1266 1580 checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" 1267 1581 dependencies = [ 1268 1582 "ff", 1269 - "rand_core", 1583 + "rand_core 0.6.4", 1270 1584 "subtle", 1271 1585 ] 1272 1586 1273 1587 [[package]] 1588 + name = "gzip-header" 1589 + version = "1.0.0" 1590 + source = "registry+https://github.com/rust-lang/crates.io-index" 1591 + checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" 1592 + dependencies = [ 1593 + "crc32fast", 1594 + ] 1595 + 1596 + [[package]] 1597 + name = "h2" 1598 + version = "0.4.10" 1599 + source = "registry+https://github.com/rust-lang/crates.io-index" 1600 + checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" 1601 + dependencies = [ 1602 + "atomic-waker", 1603 + "bytes", 1604 + "fnv", 1605 + "futures-core", 1606 + "futures-sink", 1607 + "http", 1608 + "indexmap 2.9.0", 1609 + "slab", 1610 + "tokio", 1611 + "tokio-util", 1612 + "tracing", 1613 + ] 1614 + 1615 + [[package]] 1274 1616 name = "half" 1275 1617 version = "1.8.3" 1276 1618 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1301 1643 "allocator-api2", 1302 1644 "equivalent", 1303 1645 "foldhash", 1646 + "serde", 1304 1647 ] 1305 1648 1306 1649 [[package]] ··· 1334 1677 checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 1335 1678 1336 1679 [[package]] 1680 + name = "hermit-abi" 1681 + version = "0.3.9" 1682 + source = "registry+https://github.com/rust-lang/crates.io-index" 1683 + checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 1684 + 1685 + [[package]] 1337 1686 name = "hex" 1338 1687 version = "0.4.3" 1339 1688 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1355 1704 "idna", 1356 1705 "ipnet", 1357 1706 "once_cell", 1358 - "rand", 1707 + "rand 0.8.5", 1359 1708 "thiserror 1.0.69", 1360 1709 "tinyvec", 1361 1710 "tokio", ··· 1376 1725 "lru-cache", 1377 1726 "once_cell", 1378 1727 "parking_lot", 1379 - "rand", 1728 + "rand 0.8.5", 1380 1729 "resolv-conf", 1381 1730 "smallvec", 1382 1731 "thiserror 1.0.69", ··· 1454 1803 "bytes", 1455 1804 "futures-channel", 1456 1805 "futures-util", 1806 + "h2", 1457 1807 "http", 1458 1808 "http-body", 1459 1809 "httparse", ··· 1466 1816 ] 1467 1817 1468 1818 [[package]] 1819 + name = "hyper-rustls" 1820 + version = "0.27.5" 1821 + source = "registry+https://github.com/rust-lang/crates.io-index" 1822 + checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" 1823 + dependencies = [ 1824 + "futures-util", 1825 + "http", 1826 + "hyper", 1827 + "hyper-util", 1828 + "rustls 0.23.27", 1829 + "rustls-pki-types", 1830 + "tokio", 1831 + "tokio-rustls", 1832 + "tower-service", 1833 + "webpki-roots 0.26.11", 1834 + ] 1835 + 1836 + [[package]] 1469 1837 name = "hyper-tls" 1470 1838 version = "0.6.0" 1471 1839 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1639 2007 ] 1640 2008 1641 2009 [[package]] 2010 + name = "impls" 2011 + version = "1.0.3" 2012 + source = "registry+https://github.com/rust-lang/crates.io-index" 2013 + checksum = "7a46645bbd70538861a90d0f26c31537cdf1e44aae99a794fb75a664b70951bc" 2014 + 2015 + [[package]] 1642 2016 name = "indexmap" 1643 2017 version = "1.9.3" 1644 2018 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1716 2090 checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 1717 2091 1718 2092 [[package]] 2093 + name = "jose" 2094 + version = "0.0.2" 2095 + source = "registry+https://github.com/rust-lang/crates.io-index" 2096 + checksum = "de791f8b277ef903d49b0c6f3f3b866569ae8cb1d0829825eea28374cc4c803c" 2097 + dependencies = [ 2098 + "base64ct", 2099 + "cfg-if", 2100 + "digest", 2101 + "ecdsa", 2102 + "ed25519-dalek", 2103 + "elliptic-curve", 2104 + "fluent-uri", 2105 + "generic-array", 2106 + "hashbrown 0.15.3", 2107 + "hmac", 2108 + "k256", 2109 + "mediatype", 2110 + "p256", 2111 + "p384", 2112 + "rand_core 0.6.4", 2113 + "rsa", 2114 + "secrecy", 2115 + "serde", 2116 + "serde-value", 2117 + "serde_json", 2118 + "sha2", 2119 + "signature", 2120 + "subtle", 2121 + "thiserror 2.0.12", 2122 + "zeroize", 2123 + ] 2124 + 2125 + [[package]] 1719 2126 name = "jose-b64" 1720 2127 version = "0.1.2" 1721 2128 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1745 2152 "jose-b64", 1746 2153 "jose-jwa", 1747 2154 "p256", 2155 + "p384", 2156 + "rsa", 1748 2157 "serde", 1749 2158 "zeroize", 1750 2159 ] ··· 1760 2169 ] 1761 2170 1762 2171 [[package]] 2172 + name = "k256" 2173 + version = "0.13.4" 2174 + source = "registry+https://github.com/rust-lang/crates.io-index" 2175 + checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" 2176 + dependencies = [ 2177 + "cfg-if", 2178 + "ecdsa", 2179 + "elliptic-curve", 2180 + "sha2", 2181 + ] 2182 + 2183 + [[package]] 2184 + name = "kdl" 2185 + version = "6.3.4" 2186 + source = "registry+https://github.com/rust-lang/crates.io-index" 2187 + checksum = "12661358400b02cbbf1fbd05f0a483335490e8a6bd1867620f2eeb78f304a22f" 2188 + dependencies = [ 2189 + "miette", 2190 + "num", 2191 + "thiserror 1.0.69", 2192 + "winnow 0.6.24", 2193 + ] 2194 + 2195 + [[package]] 1763 2196 name = "langtag" 1764 2197 version = "0.3.4" 1765 2198 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1773 2206 version = "1.5.0" 1774 2207 source = "registry+https://github.com/rust-lang/crates.io-index" 1775 2208 checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 2209 + dependencies = [ 2210 + "spin 0.9.8", 2211 + ] 1776 2212 1777 2213 [[package]] 1778 2214 name = "lexical-parse-float" ··· 1811 2247 checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 1812 2248 1813 2249 [[package]] 2250 + name = "libm" 2251 + version = "0.2.15" 2252 + source = "registry+https://github.com/rust-lang/crates.io-index" 2253 + checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" 2254 + 2255 + [[package]] 2256 + name = "libredox" 2257 + version = "0.1.3" 2258 + source = "registry+https://github.com/rust-lang/crates.io-index" 2259 + checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" 2260 + dependencies = [ 2261 + "bitflags 2.9.0", 2262 + "libc", 2263 + "redox_syscall", 2264 + ] 2265 + 2266 + [[package]] 1814 2267 name = "libsqlite3-sys" 1815 2268 version = "0.30.1" 1816 2269 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1891 2344 version = "0.13.0" 1892 2345 source = "git+https://github.com/rsform/markdown-weaver#34bc7ad2d7e6e9b9604ba61602b68589f271404e" 1893 2346 dependencies = [ 1894 - "bitflags", 2347 + "bitflags 2.9.0", 1895 2348 "getopts", 1896 2349 "markdown-weaver-escape", 1897 2350 "memchr", ··· 1926 2379 checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" 1927 2380 1928 2381 [[package]] 2382 + name = "md-5" 2383 + version = "0.10.6" 2384 + source = "registry+https://github.com/rust-lang/crates.io-index" 2385 + checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" 2386 + dependencies = [ 2387 + "cfg-if", 2388 + "digest", 2389 + ] 2390 + 2391 + [[package]] 2392 + name = "mediatype" 2393 + version = "0.19.20" 2394 + source = "registry+https://github.com/rust-lang/crates.io-index" 2395 + checksum = "33746aadcb41349ec291e7f2f0a3aa6834d1d7c58066fb4b01f68efc4c4b7631" 2396 + dependencies = [ 2397 + "serde", 2398 + ] 2399 + 2400 + [[package]] 1929 2401 name = "memchr" 1930 2402 version = "2.7.4" 1931 2403 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1952 2424 "camino", 1953 2425 "compact_bytes", 1954 2426 "compact_str", 1955 - "ordered-float", 2427 + "ordered-float 4.6.0", 1956 2428 "rusqlite", 2429 + "serde", 1957 2430 "time", 1958 2431 ] 1959 2432 ··· 2003 2476 "supports-color", 2004 2477 "supports-hyperlinks", 2005 2478 "supports-unicode", 2479 + "syntect", 2006 2480 "terminal_size", 2007 2481 "textwrap", 2008 2482 "unicode-width 0.1.14", ··· 2020 2494 ] 2021 2495 2022 2496 [[package]] 2497 + name = "migrations_internals" 2498 + version = "2.2.0" 2499 + source = "registry+https://github.com/rust-lang/crates.io-index" 2500 + checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff" 2501 + dependencies = [ 2502 + "serde", 2503 + "toml", 2504 + ] 2505 + 2506 + [[package]] 2507 + name = "migrations_macros" 2508 + version = "2.2.0" 2509 + source = "registry+https://github.com/rust-lang/crates.io-index" 2510 + checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd" 2511 + dependencies = [ 2512 + "migrations_internals", 2513 + "proc-macro2", 2514 + "quote", 2515 + ] 2516 + 2517 + [[package]] 2023 2518 name = "mime" 2024 2519 version = "0.3.17" 2025 2520 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2041 2536 source = "registry+https://github.com/rust-lang/crates.io-index" 2042 2537 checksum = "dd72e8b4e42274540edabec853f607c015c73436159b06c39c7af85a20433155" 2043 2538 dependencies = [ 2539 + "serde", 2540 + ] 2541 + 2542 + [[package]] 2543 + name = "minijinja-contrib" 2544 + version = "2.10.2" 2545 + source = "registry+https://github.com/rust-lang/crates.io-index" 2546 + checksum = "457f85f9c4c5b17d11fcf9bbe7c0dbba64843c5ee040005956f1a510b6679fe2" 2547 + dependencies = [ 2548 + "minijinja", 2044 2549 "serde", 2045 2550 ] 2046 2551 ··· 2109 2614 ] 2110 2615 2111 2616 [[package]] 2617 + name = "multipart" 2618 + version = "0.18.0" 2619 + source = "registry+https://github.com/rust-lang/crates.io-index" 2620 + checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" 2621 + dependencies = [ 2622 + "buf_redux", 2623 + "httparse", 2624 + "log", 2625 + "mime", 2626 + "mime_guess", 2627 + "quick-error", 2628 + "rand 0.8.5", 2629 + "safemem", 2630 + "tempfile", 2631 + "twoway", 2632 + ] 2633 + 2634 + [[package]] 2635 + name = "mutants" 2636 + version = "0.0.3" 2637 + source = "registry+https://github.com/rust-lang/crates.io-index" 2638 + checksum = "bc0287524726960e07b119cebd01678f852f147742ae0d925e6a520dca956126" 2639 + 2640 + [[package]] 2112 2641 name = "n0-future" 2113 2642 version = "0.1.3" 2114 2643 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2157 2686 ] 2158 2687 2159 2688 [[package]] 2689 + name = "num" 2690 + version = "0.4.3" 2691 + source = "registry+https://github.com/rust-lang/crates.io-index" 2692 + checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" 2693 + dependencies = [ 2694 + "num-bigint", 2695 + "num-complex", 2696 + "num-integer", 2697 + "num-iter", 2698 + "num-rational", 2699 + "num-traits", 2700 + ] 2701 + 2702 + [[package]] 2703 + name = "num-bigint" 2704 + version = "0.4.6" 2705 + source = "registry+https://github.com/rust-lang/crates.io-index" 2706 + checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 2707 + dependencies = [ 2708 + "num-integer", 2709 + "num-traits", 2710 + ] 2711 + 2712 + [[package]] 2713 + name = "num-bigint-dig" 2714 + version = "0.8.4" 2715 + source = "registry+https://github.com/rust-lang/crates.io-index" 2716 + checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" 2717 + dependencies = [ 2718 + "byteorder", 2719 + "lazy_static", 2720 + "libm", 2721 + "num-integer", 2722 + "num-iter", 2723 + "num-traits", 2724 + "rand 0.8.5", 2725 + "smallvec", 2726 + "zeroize", 2727 + ] 2728 + 2729 + [[package]] 2730 + name = "num-complex" 2731 + version = "0.4.6" 2732 + source = "registry+https://github.com/rust-lang/crates.io-index" 2733 + checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" 2734 + dependencies = [ 2735 + "num-traits", 2736 + ] 2737 + 2738 + [[package]] 2160 2739 name = "num-conv" 2161 2740 version = "0.1.0" 2162 2741 source = "registry+https://github.com/rust-lang/crates.io-index" 2163 2742 checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 2164 2743 2165 2744 [[package]] 2745 + name = "num-integer" 2746 + version = "0.1.46" 2747 + source = "registry+https://github.com/rust-lang/crates.io-index" 2748 + checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 2749 + dependencies = [ 2750 + "num-traits", 2751 + ] 2752 + 2753 + [[package]] 2754 + name = "num-iter" 2755 + version = "0.1.45" 2756 + source = "registry+https://github.com/rust-lang/crates.io-index" 2757 + checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 2758 + dependencies = [ 2759 + "autocfg", 2760 + "num-integer", 2761 + "num-traits", 2762 + ] 2763 + 2764 + [[package]] 2765 + name = "num-rational" 2766 + version = "0.4.2" 2767 + source = "registry+https://github.com/rust-lang/crates.io-index" 2768 + checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" 2769 + dependencies = [ 2770 + "num-bigint", 2771 + "num-integer", 2772 + "num-traits", 2773 + ] 2774 + 2775 + [[package]] 2166 2776 name = "num-traits" 2167 2777 version = "0.2.19" 2168 2778 source = "registry+https://github.com/rust-lang/crates.io-index" 2169 2779 checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 2170 2780 dependencies = [ 2171 2781 "autocfg", 2782 + "libm", 2783 + ] 2784 + 2785 + [[package]] 2786 + name = "num_cpus" 2787 + version = "1.16.0" 2788 + source = "registry+https://github.com/rust-lang/crates.io-index" 2789 + checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 2790 + dependencies = [ 2791 + "hermit-abi", 2792 + "libc", 2793 + ] 2794 + 2795 + [[package]] 2796 + name = "num_threads" 2797 + version = "0.1.7" 2798 + source = "registry+https://github.com/rust-lang/crates.io-index" 2799 + checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" 2800 + dependencies = [ 2801 + "libc", 2172 2802 ] 2173 2803 2174 2804 [[package]] ··· 2187 2817 checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 2188 2818 2189 2819 [[package]] 2820 + name = "onig" 2821 + version = "6.4.0" 2822 + source = "registry+https://github.com/rust-lang/crates.io-index" 2823 + checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" 2824 + dependencies = [ 2825 + "bitflags 1.3.2", 2826 + "libc", 2827 + "once_cell", 2828 + "onig_sys", 2829 + ] 2830 + 2831 + [[package]] 2832 + name = "onig_sys" 2833 + version = "69.8.1" 2834 + source = "registry+https://github.com/rust-lang/crates.io-index" 2835 + checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" 2836 + dependencies = [ 2837 + "cc", 2838 + "pkg-config", 2839 + ] 2840 + 2841 + [[package]] 2190 2842 name = "openssl" 2191 2843 version = "0.10.72" 2192 2844 source = "registry+https://github.com/rust-lang/crates.io-index" 2193 2845 checksum = "fedfea7d58a1f73118430a55da6a286e7b044961736ce96a16a17068ea25e5da" 2194 2846 dependencies = [ 2195 - "bitflags", 2847 + "bitflags 2.9.0", 2196 2848 "cfg-if", 2197 2849 "foreign-types", 2198 2850 "libc", ··· 2232 2884 2233 2885 [[package]] 2234 2886 name = "ordered-float" 2887 + version = "2.10.1" 2888 + source = "registry+https://github.com/rust-lang/crates.io-index" 2889 + checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" 2890 + dependencies = [ 2891 + "num-traits", 2892 + ] 2893 + 2894 + [[package]] 2895 + name = "ordered-float" 2235 2896 version = "4.6.0" 2236 2897 source = "registry+https://github.com/rust-lang/crates.io-index" 2237 2898 checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" ··· 2264 2925 ] 2265 2926 2266 2927 [[package]] 2928 + name = "p384" 2929 + version = "0.13.1" 2930 + source = "registry+https://github.com/rust-lang/crates.io-index" 2931 + checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" 2932 + dependencies = [ 2933 + "ecdsa", 2934 + "elliptic-curve", 2935 + "primeorder", 2936 + "sha2", 2937 + ] 2938 + 2939 + [[package]] 2267 2940 name = "parking" 2268 2941 version = "2.2.1" 2269 2942 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2299 2972 checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 2300 2973 2301 2974 [[package]] 2975 + name = "pem-rfc7468" 2976 + version = "0.7.0" 2977 + source = "registry+https://github.com/rust-lang/crates.io-index" 2978 + checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" 2979 + dependencies = [ 2980 + "base64ct", 2981 + ] 2982 + 2983 + [[package]] 2302 2984 name = "percent-encoding" 2303 2985 version = "2.3.1" 2304 2986 source = "registry+https://github.com/rust-lang/crates.io-index" 2305 2987 checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 2988 + 2989 + [[package]] 2990 + name = "phf" 2991 + version = "0.11.3" 2992 + source = "registry+https://github.com/rust-lang/crates.io-index" 2993 + checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" 2994 + dependencies = [ 2995 + "phf_shared", 2996 + ] 2997 + 2998 + [[package]] 2999 + name = "phf_shared" 3000 + version = "0.11.3" 3001 + source = "registry+https://github.com/rust-lang/crates.io-index" 3002 + checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" 3003 + dependencies = [ 3004 + "siphasher", 3005 + ] 2306 3006 2307 3007 [[package]] 2308 3008 name = "pin-project" ··· 2337 3037 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 2338 3038 2339 3039 [[package]] 3040 + name = "pkcs1" 3041 + version = "0.7.5" 3042 + source = "registry+https://github.com/rust-lang/crates.io-index" 3043 + checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" 3044 + dependencies = [ 3045 + "der", 3046 + "pkcs8", 3047 + "spki", 3048 + ] 3049 + 3050 + [[package]] 3051 + name = "pkcs8" 3052 + version = "0.10.2" 3053 + source = "registry+https://github.com/rust-lang/crates.io-index" 3054 + checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" 3055 + dependencies = [ 3056 + "der", 3057 + "spki", 3058 + ] 3059 + 3060 + [[package]] 2340 3061 name = "pkg-config" 2341 3062 version = "0.3.32" 2342 3063 source = "registry+https://github.com/rust-lang/crates.io-index" 2343 3064 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 2344 3065 2345 3066 [[package]] 3067 + name = "plist" 3068 + version = "1.7.1" 3069 + source = "registry+https://github.com/rust-lang/crates.io-index" 3070 + checksum = "eac26e981c03a6e53e0aee43c113e3202f5581d5360dae7bd2c70e800dd0451d" 3071 + dependencies = [ 3072 + "base64 0.22.1", 3073 + "indexmap 2.9.0", 3074 + "quick-xml", 3075 + "serde", 3076 + "time", 3077 + ] 3078 + 3079 + [[package]] 2346 3080 name = "portable-atomic" 2347 3081 version = "1.11.0" 2348 3082 source = "registry+https://github.com/rust-lang/crates.io-index" 2349 3083 checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" 2350 3084 2351 3085 [[package]] 3086 + name = "postgres-protocol" 3087 + version = "0.6.8" 3088 + source = "registry+https://github.com/rust-lang/crates.io-index" 3089 + checksum = "76ff0abab4a9b844b93ef7b81f1efc0a366062aaef2cd702c76256b5dc075c54" 3090 + dependencies = [ 3091 + "base64 0.22.1", 3092 + "byteorder", 3093 + "bytes", 3094 + "fallible-iterator 0.2.0", 3095 + "hmac", 3096 + "md-5", 3097 + "memchr", 3098 + "rand 0.9.1", 3099 + "sha2", 3100 + "stringprep", 3101 + ] 3102 + 3103 + [[package]] 3104 + name = "postgres-types" 3105 + version = "0.2.9" 3106 + source = "registry+https://github.com/rust-lang/crates.io-index" 3107 + checksum = "613283563cd90e1dfc3518d548caee47e0e725455ed619881f5cf21f36de4b48" 3108 + dependencies = [ 3109 + "bytes", 3110 + "fallible-iterator 0.2.0", 3111 + "postgres-protocol", 3112 + ] 3113 + 3114 + [[package]] 2352 3115 name = "potential_utf" 2353 3116 version = "0.1.2" 2354 3117 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2373 3136 ] 2374 3137 2375 3138 [[package]] 3139 + name = "pq-sys" 3140 + version = "0.7.1" 3141 + source = "registry+https://github.com/rust-lang/crates.io-index" 3142 + checksum = "41c852911b98f5981956037b2ca976660612e548986c30af075e753107bc3400" 3143 + dependencies = [ 3144 + "libc", 3145 + "vcpkg", 3146 + ] 3147 + 3148 + [[package]] 2376 3149 name = "prettyplease" 2377 3150 version = "0.2.32" 2378 3151 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2401 3174 ] 2402 3175 2403 3176 [[package]] 3177 + name = "quick-error" 3178 + version = "1.2.3" 3179 + source = "registry+https://github.com/rust-lang/crates.io-index" 3180 + checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 3181 + 3182 + [[package]] 3183 + name = "quick-xml" 3184 + version = "0.32.0" 3185 + source = "registry+https://github.com/rust-lang/crates.io-index" 3186 + checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" 3187 + dependencies = [ 3188 + "memchr", 3189 + ] 3190 + 3191 + [[package]] 3192 + name = "quinn" 3193 + version = "0.11.6" 3194 + source = "registry+https://github.com/rust-lang/crates.io-index" 3195 + checksum = "62e96808277ec6f97351a2380e6c25114bc9e67037775464979f3037c92d05ef" 3196 + dependencies = [ 3197 + "bytes", 3198 + "pin-project-lite", 3199 + "quinn-proto", 3200 + "quinn-udp", 3201 + "rustc-hash", 3202 + "rustls 0.23.27", 3203 + "socket2", 3204 + "thiserror 2.0.12", 3205 + "tokio", 3206 + "tracing", 3207 + ] 3208 + 3209 + [[package]] 3210 + name = "quinn-proto" 3211 + version = "0.11.9" 3212 + source = "registry+https://github.com/rust-lang/crates.io-index" 3213 + checksum = "a2fe5ef3495d7d2e377ff17b1a8ce2ee2ec2a18cde8b6ad6619d65d0701c135d" 3214 + dependencies = [ 3215 + "bytes", 3216 + "getrandom 0.2.16", 3217 + "rand 0.8.5", 3218 + "ring 0.17.14", 3219 + "rustc-hash", 3220 + "rustls 0.23.27", 3221 + "rustls-pki-types", 3222 + "slab", 3223 + "thiserror 2.0.12", 3224 + "tinyvec", 3225 + "tracing", 3226 + "web-time", 3227 + ] 3228 + 3229 + [[package]] 3230 + name = "quinn-udp" 3231 + version = "0.5.9" 3232 + source = "registry+https://github.com/rust-lang/crates.io-index" 3233 + checksum = "1c40286217b4ba3a71d644d752e6a0b71f13f1b6a2c5311acfcbe0c2418ed904" 3234 + dependencies = [ 3235 + "cfg_aliases", 3236 + "libc", 3237 + "once_cell", 3238 + "socket2", 3239 + "tracing", 3240 + "windows-sys 0.59.0", 3241 + ] 3242 + 3243 + [[package]] 2404 3244 name = "quote" 2405 3245 version = "1.0.40" 2406 3246 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2422 3262 checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 2423 3263 dependencies = [ 2424 3264 "libc", 2425 - "rand_chacha", 2426 - "rand_core", 3265 + "rand_chacha 0.3.1", 3266 + "rand_core 0.6.4", 3267 + ] 3268 + 3269 + [[package]] 3270 + name = "rand" 3271 + version = "0.9.1" 3272 + source = "registry+https://github.com/rust-lang/crates.io-index" 3273 + checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" 3274 + dependencies = [ 3275 + "rand_chacha 0.9.0", 3276 + "rand_core 0.9.3", 2427 3277 ] 2428 3278 2429 3279 [[package]] ··· 2433 3283 checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 2434 3284 dependencies = [ 2435 3285 "ppv-lite86", 2436 - "rand_core", 3286 + "rand_core 0.6.4", 3287 + ] 3288 + 3289 + [[package]] 3290 + name = "rand_chacha" 3291 + version = "0.9.0" 3292 + source = "registry+https://github.com/rust-lang/crates.io-index" 3293 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 3294 + dependencies = [ 3295 + "ppv-lite86", 3296 + "rand_core 0.9.3", 2437 3297 ] 2438 3298 2439 3299 [[package]] ··· 2446 3306 ] 2447 3307 2448 3308 [[package]] 3309 + name = "rand_core" 3310 + version = "0.9.3" 3311 + source = "registry+https://github.com/rust-lang/crates.io-index" 3312 + checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 3313 + dependencies = [ 3314 + "getrandom 0.3.3", 3315 + ] 3316 + 3317 + [[package]] 2449 3318 name = "redox_syscall" 2450 3319 version = "0.5.12" 2451 3320 source = "registry+https://github.com/rust-lang/crates.io-index" 2452 3321 checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" 2453 3322 dependencies = [ 2454 - "bitflags", 3323 + "bitflags 2.9.0", 3324 + ] 3325 + 3326 + [[package]] 3327 + name = "ref-cast" 3328 + version = "1.0.24" 3329 + source = "registry+https://github.com/rust-lang/crates.io-index" 3330 + checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" 3331 + dependencies = [ 3332 + "ref-cast-impl", 3333 + ] 3334 + 3335 + [[package]] 3336 + name = "ref-cast-impl" 3337 + version = "1.0.24" 3338 + source = "registry+https://github.com/rust-lang/crates.io-index" 3339 + checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" 3340 + dependencies = [ 3341 + "proc-macro2", 3342 + "quote", 3343 + "syn", 2455 3344 ] 2456 3345 2457 3346 [[package]] ··· 2507 3396 "async-compression", 2508 3397 "base64 0.22.1", 2509 3398 "bytes", 3399 + "encoding_rs", 2510 3400 "futures-core", 2511 3401 "futures-util", 3402 + "h2", 2512 3403 "http", 2513 3404 "http-body", 2514 3405 "http-body-util", 2515 3406 "hyper", 3407 + "hyper-rustls", 2516 3408 "hyper-tls", 2517 3409 "hyper-util", 2518 3410 "ipnet", ··· 2523 3415 "once_cell", 2524 3416 "percent-encoding", 2525 3417 "pin-project-lite", 2526 - "rustls-pemfile", 3418 + "quinn", 3419 + "rustls 0.23.27", 3420 + "rustls-pemfile 2.2.0", 3421 + "rustls-pki-types", 2527 3422 "serde", 2528 3423 "serde_json", 2529 3424 "serde_urlencoded", 2530 3425 "sync_wrapper", 3426 + "system-configuration", 2531 3427 "tokio", 2532 3428 "tokio-native-tls", 3429 + "tokio-rustls", 2533 3430 "tokio-util", 2534 3431 "tower", 2535 3432 "tower-service", ··· 2537 3434 "wasm-bindgen", 2538 3435 "wasm-bindgen-futures", 2539 3436 "web-sys", 3437 + "webpki-roots 0.26.11", 2540 3438 "windows-registry", 2541 3439 ] 2542 3440 ··· 2557 3455 ] 2558 3456 2559 3457 [[package]] 3458 + name = "ring" 3459 + version = "0.16.20" 3460 + source = "registry+https://github.com/rust-lang/crates.io-index" 3461 + checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" 3462 + dependencies = [ 3463 + "cc", 3464 + "libc", 3465 + "once_cell", 3466 + "spin 0.5.2", 3467 + "untrusted 0.7.1", 3468 + "web-sys", 3469 + "winapi", 3470 + ] 3471 + 3472 + [[package]] 3473 + name = "ring" 3474 + version = "0.17.14" 3475 + source = "registry+https://github.com/rust-lang/crates.io-index" 3476 + checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 3477 + dependencies = [ 3478 + "cc", 3479 + "cfg-if", 3480 + "getrandom 0.2.16", 3481 + "libc", 3482 + "untrusted 0.9.0", 3483 + "windows-sys 0.52.0", 3484 + ] 3485 + 3486 + [[package]] 2560 3487 name = "rmp" 2561 3488 version = "0.8.14" 2562 3489 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2568 3495 ] 2569 3496 2570 3497 [[package]] 3498 + name = "rouille" 3499 + version = "3.6.2" 3500 + source = "registry+https://github.com/rust-lang/crates.io-index" 3501 + checksum = "3716fbf57fc1084d7a706adf4e445298d123e4a44294c4e8213caf1b85fcc921" 3502 + dependencies = [ 3503 + "base64 0.13.1", 3504 + "brotli", 3505 + "chrono", 3506 + "deflate", 3507 + "filetime", 3508 + "multipart", 3509 + "percent-encoding", 3510 + "rand 0.8.5", 3511 + "serde", 3512 + "serde_derive", 3513 + "serde_json", 3514 + "sha1_smol", 3515 + "threadpool", 3516 + "time", 3517 + "tiny_http", 3518 + "url", 3519 + ] 3520 + 3521 + [[package]] 3522 + name = "rsa" 3523 + version = "0.9.8" 3524 + source = "registry+https://github.com/rust-lang/crates.io-index" 3525 + checksum = "78928ac1ed176a5ca1d17e578a1825f3d81ca54cf41053a592584b020cfd691b" 3526 + dependencies = [ 3527 + "const-oid", 3528 + "digest", 3529 + "num-bigint-dig", 3530 + "num-integer", 3531 + "num-traits", 3532 + "pkcs1", 3533 + "pkcs8", 3534 + "rand_core 0.6.4", 3535 + "signature", 3536 + "spki", 3537 + "subtle", 3538 + "zeroize", 3539 + ] 3540 + 3541 + [[package]] 2571 3542 name = "rusqlite" 2572 3543 version = "0.32.1" 2573 3544 source = "registry+https://github.com/rust-lang/crates.io-index" 2574 3545 checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" 2575 3546 dependencies = [ 2576 - "bitflags", 2577 - "fallible-iterator", 3547 + "bitflags 2.9.0", 3548 + "fallible-iterator 0.3.0", 2578 3549 "fallible-streaming-iterator", 2579 3550 "hashlink 0.9.1", 2580 3551 "libsqlite3-sys", ··· 2588 3559 checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 2589 3560 2590 3561 [[package]] 3562 + name = "rustc-hash" 3563 + version = "2.1.1" 3564 + source = "registry+https://github.com/rust-lang/crates.io-index" 3565 + checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" 3566 + 3567 + [[package]] 2591 3568 name = "rustc_version" 2592 3569 version = "0.4.1" 2593 3570 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2602 3579 source = "registry+https://github.com/rust-lang/crates.io-index" 2603 3580 checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" 2604 3581 dependencies = [ 2605 - "bitflags", 3582 + "bitflags 2.9.0", 2606 3583 "errno", 2607 3584 "libc", 2608 3585 "linux-raw-sys", ··· 2610 3587 ] 2611 3588 2612 3589 [[package]] 3590 + name = "rustls" 3591 + version = "0.20.9" 3592 + source = "registry+https://github.com/rust-lang/crates.io-index" 3593 + checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" 3594 + dependencies = [ 3595 + "log", 3596 + "ring 0.16.20", 3597 + "sct", 3598 + "webpki", 3599 + ] 3600 + 3601 + [[package]] 3602 + name = "rustls" 3603 + version = "0.23.27" 3604 + source = "registry+https://github.com/rust-lang/crates.io-index" 3605 + checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" 3606 + dependencies = [ 3607 + "once_cell", 3608 + "ring 0.17.14", 3609 + "rustls-pki-types", 3610 + "rustls-webpki", 3611 + "subtle", 3612 + "zeroize", 3613 + ] 3614 + 3615 + [[package]] 3616 + name = "rustls-pemfile" 3617 + version = "0.2.1" 3618 + source = "registry+https://github.com/rust-lang/crates.io-index" 3619 + checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" 3620 + dependencies = [ 3621 + "base64 0.13.1", 3622 + ] 3623 + 3624 + [[package]] 2613 3625 name = "rustls-pemfile" 2614 3626 version = "2.2.0" 2615 3627 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2624 3636 source = "registry+https://github.com/rust-lang/crates.io-index" 2625 3637 checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" 2626 3638 dependencies = [ 3639 + "web-time", 2627 3640 "zeroize", 3641 + ] 3642 + 3643 + [[package]] 3644 + name = "rustls-webpki" 3645 + version = "0.103.3" 3646 + source = "registry+https://github.com/rust-lang/crates.io-index" 3647 + checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" 3648 + dependencies = [ 3649 + "ring 0.17.14", 3650 + "rustls-pki-types", 3651 + "untrusted 0.9.0", 2628 3652 ] 2629 3653 2630 3654 [[package]] ··· 2640 3664 checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 2641 3665 2642 3666 [[package]] 3667 + name = "safemem" 3668 + version = "0.3.3" 3669 + source = "registry+https://github.com/rust-lang/crates.io-index" 3670 + checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" 3671 + 3672 + [[package]] 3673 + name = "same-file" 3674 + version = "1.0.6" 3675 + source = "registry+https://github.com/rust-lang/crates.io-index" 3676 + checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 3677 + dependencies = [ 3678 + "winapi-util", 3679 + ] 3680 + 3681 + [[package]] 2643 3682 name = "schannel" 2644 3683 version = "0.1.27" 2645 3684 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2649 3688 ] 2650 3689 2651 3690 [[package]] 3691 + name = "scoped-futures" 3692 + version = "0.1.4" 3693 + source = "registry+https://github.com/rust-lang/crates.io-index" 3694 + checksum = "1b24aae2d0636530f359e9d5ef0c04669d11c5e756699b27a6a6d845d8329091" 3695 + dependencies = [ 3696 + "pin-project-lite", 3697 + ] 3698 + 3699 + [[package]] 2652 3700 name = "scoped-tls" 2653 3701 version = "1.0.1" 2654 3702 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2659 3707 version = "1.2.0" 2660 3708 source = "registry+https://github.com/rust-lang/crates.io-index" 2661 3709 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 3710 + 3711 + [[package]] 3712 + name = "sct" 3713 + version = "0.7.1" 3714 + source = "registry+https://github.com/rust-lang/crates.io-index" 3715 + checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" 3716 + dependencies = [ 3717 + "ring 0.17.14", 3718 + "untrusted 0.9.0", 3719 + ] 2662 3720 2663 3721 [[package]] 2664 3722 name = "sec1" ··· 2669 3727 "base16ct", 2670 3728 "der", 2671 3729 "generic-array", 3730 + "pkcs8", 3731 + "serdect", 2672 3732 "subtle", 2673 3733 "zeroize", 2674 3734 ] 2675 3735 2676 3736 [[package]] 3737 + name = "secrecy" 3738 + version = "0.10.3" 3739 + source = "registry+https://github.com/rust-lang/crates.io-index" 3740 + checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" 3741 + dependencies = [ 3742 + "serde", 3743 + "zeroize", 3744 + ] 3745 + 3746 + [[package]] 2677 3747 name = "security-framework" 2678 3748 version = "2.11.1" 2679 3749 source = "registry+https://github.com/rust-lang/crates.io-index" 2680 3750 checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" 2681 3751 dependencies = [ 2682 - "bitflags", 3752 + "bitflags 2.9.0", 2683 3753 "core-foundation", 2684 3754 "core-foundation-sys", 2685 3755 "libc", ··· 2718 3788 ] 2719 3789 2720 3790 [[package]] 3791 + name = "serde-value" 3792 + version = "0.7.0" 3793 + source = "registry+https://github.com/rust-lang/crates.io-index" 3794 + checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" 3795 + dependencies = [ 3796 + "ordered-float 2.10.1", 3797 + "serde", 3798 + ] 3799 + 3800 + [[package]] 2721 3801 name = "serde_bytes" 2722 3802 version = "0.11.17" 2723 3803 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2856 3936 ] 2857 3937 2858 3938 [[package]] 3939 + name = "serdect" 3940 + version = "0.2.0" 3941 + source = "registry+https://github.com/rust-lang/crates.io-index" 3942 + checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" 3943 + dependencies = [ 3944 + "base16ct", 3945 + "serde", 3946 + ] 3947 + 3948 + [[package]] 3949 + name = "sha1_smol" 3950 + version = "1.0.1" 3951 + source = "registry+https://github.com/rust-lang/crates.io-index" 3952 + checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d" 3953 + 3954 + [[package]] 2859 3955 name = "sha2" 2860 3956 version = "0.10.9" 2861 3957 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2865 3961 "cpufeatures", 2866 3962 "digest", 2867 3963 ] 3964 + 3965 + [[package]] 3966 + name = "shadow_counted" 3967 + version = "0.4.0" 3968 + source = "registry+https://github.com/rust-lang/crates.io-index" 3969 + checksum = "65da48d447333cebe1aadbdd3662f3ba56e76e67f53bc46f3dd5f67c74629d6b" 2868 3970 2869 3971 [[package]] 2870 3972 name = "sharded-slab" ··· 2897 3999 checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" 2898 4000 dependencies = [ 2899 4001 "digest", 2900 - "rand_core", 4002 + "rand_core 0.6.4", 2901 4003 ] 4004 + 4005 + [[package]] 4006 + name = "siphasher" 4007 + version = "1.0.1" 4008 + source = "registry+https://github.com/rust-lang/crates.io-index" 4009 + checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" 2902 4010 2903 4011 [[package]] 2904 4012 name = "slab" ··· 2927 4035 2928 4036 [[package]] 2929 4037 name = "spin" 4038 + version = "0.5.2" 4039 + source = "registry+https://github.com/rust-lang/crates.io-index" 4040 + checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" 4041 + 4042 + [[package]] 4043 + name = "spin" 2930 4044 version = "0.9.8" 2931 4045 source = "registry+https://github.com/rust-lang/crates.io-index" 2932 4046 checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 4047 + 4048 + [[package]] 4049 + name = "spki" 4050 + version = "0.7.3" 4051 + source = "registry+https://github.com/rust-lang/crates.io-index" 4052 + checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" 4053 + dependencies = [ 4054 + "base64ct", 4055 + "der", 4056 + ] 2933 4057 2934 4058 [[package]] 2935 4059 name = "stable_deref_trait" ··· 2944 4068 checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 2945 4069 2946 4070 [[package]] 4071 + name = "stringprep" 4072 + version = "0.1.5" 4073 + source = "registry+https://github.com/rust-lang/crates.io-index" 4074 + checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" 4075 + dependencies = [ 4076 + "unicode-bidi", 4077 + "unicode-normalization", 4078 + "unicode-properties", 4079 + ] 4080 + 4081 + [[package]] 2947 4082 name = "strsim" 2948 4083 version = "0.11.1" 2949 4084 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3008 4143 ] 3009 4144 3010 4145 [[package]] 4146 + name = "syntect" 4147 + version = "5.2.0" 4148 + source = "registry+https://github.com/rust-lang/crates.io-index" 4149 + checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" 4150 + dependencies = [ 4151 + "bincode", 4152 + "bitflags 1.3.2", 4153 + "flate2", 4154 + "fnv", 4155 + "once_cell", 4156 + "onig", 4157 + "plist", 4158 + "regex-syntax 0.8.5", 4159 + "serde", 4160 + "serde_derive", 4161 + "serde_json", 4162 + "thiserror 1.0.69", 4163 + "walkdir", 4164 + "yaml-rust", 4165 + ] 4166 + 4167 + [[package]] 4168 + name = "system-configuration" 4169 + version = "0.6.1" 4170 + source = "registry+https://github.com/rust-lang/crates.io-index" 4171 + checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" 4172 + dependencies = [ 4173 + "bitflags 2.9.0", 4174 + "core-foundation", 4175 + "system-configuration-sys", 4176 + ] 4177 + 4178 + [[package]] 4179 + name = "system-configuration-sys" 4180 + version = "0.6.0" 4181 + source = "registry+https://github.com/rust-lang/crates.io-index" 4182 + checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 4183 + dependencies = [ 4184 + "core-foundation-sys", 4185 + "libc", 4186 + ] 4187 + 4188 + [[package]] 3011 4189 name = "tagptr" 3012 4190 version = "0.2.0" 3013 4191 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3097 4275 ] 3098 4276 3099 4277 [[package]] 4278 + name = "threadpool" 4279 + version = "1.8.1" 4280 + source = "registry+https://github.com/rust-lang/crates.io-index" 4281 + checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" 4282 + dependencies = [ 4283 + "num_cpus", 4284 + ] 4285 + 4286 + [[package]] 3100 4287 name = "time" 3101 4288 version = "0.3.41" 3102 4289 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3104 4291 dependencies = [ 3105 4292 "deranged", 3106 4293 "itoa", 4294 + "libc", 3107 4295 "num-conv", 4296 + "num_threads", 3108 4297 "powerfmt", 3109 4298 "serde", 3110 4299 "time-core", ··· 3128 4317 ] 3129 4318 3130 4319 [[package]] 4320 + name = "tiny_http" 4321 + version = "0.12.0" 4322 + source = "registry+https://github.com/rust-lang/crates.io-index" 4323 + checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" 4324 + dependencies = [ 4325 + "ascii", 4326 + "chunked_transfer", 4327 + "httpdate", 4328 + "log", 4329 + "rustls 0.20.9", 4330 + "rustls-pemfile 0.2.1", 4331 + "zeroize", 4332 + ] 4333 + 4334 + [[package]] 3131 4335 name = "tinystr" 3132 4336 version = "0.8.1" 3133 4337 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3192 4396 ] 3193 4397 3194 4398 [[package]] 4399 + name = "tokio-postgres" 4400 + version = "0.7.13" 4401 + source = "registry+https://github.com/rust-lang/crates.io-index" 4402 + checksum = "6c95d533c83082bb6490e0189acaa0bbeef9084e60471b696ca6988cd0541fb0" 4403 + dependencies = [ 4404 + "async-trait", 4405 + "byteorder", 4406 + "bytes", 4407 + "fallible-iterator 0.2.0", 4408 + "futures-channel", 4409 + "futures-util", 4410 + "log", 4411 + "parking_lot", 4412 + "percent-encoding", 4413 + "phf", 4414 + "pin-project-lite", 4415 + "postgres-protocol", 4416 + "postgres-types", 4417 + "rand 0.9.1", 4418 + "socket2", 4419 + "tokio", 4420 + "tokio-util", 4421 + "whoami", 4422 + ] 4423 + 4424 + [[package]] 4425 + name = "tokio-rustls" 4426 + version = "0.26.2" 4427 + source = "registry+https://github.com/rust-lang/crates.io-index" 4428 + checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" 4429 + dependencies = [ 4430 + "rustls 0.23.27", 4431 + "tokio", 4432 + ] 4433 + 4434 + [[package]] 3195 4435 name = "tokio-util" 3196 4436 version = "0.7.15" 3197 4437 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3212 4452 source = "registry+https://github.com/rust-lang/crates.io-index" 3213 4453 checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" 3214 4454 dependencies = [ 4455 + "indexmap 2.9.0", 3215 4456 "serde", 3216 4457 "serde_spanned", 3217 4458 "toml_datetime", ··· 3238 4479 "serde_spanned", 3239 4480 "toml_datetime", 3240 4481 "toml_write", 3241 - "winnow", 4482 + "winnow 0.7.10", 3242 4483 ] 3243 4484 3244 4485 [[package]] ··· 3285 4526 source = "registry+https://github.com/rust-lang/crates.io-index" 3286 4527 checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" 3287 4528 dependencies = [ 3288 - "bitflags", 4529 + "bitflags 2.9.0", 3289 4530 "bytes", 3290 4531 "futures-util", 3291 4532 "http", ··· 3302 4543 "tower-layer", 3303 4544 "tower-service", 3304 4545 "tracing", 4546 + "uuid", 3305 4547 ] 3306 4548 3307 4549 [[package]] ··· 3329 4571 ] 3330 4572 3331 4573 [[package]] 4574 + name = "tracing-appender" 4575 + version = "0.2.3" 4576 + source = "registry+https://github.com/rust-lang/crates.io-index" 4577 + checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" 4578 + dependencies = [ 4579 + "crossbeam-channel", 4580 + "parking_lot", 4581 + "thiserror 1.0.69", 4582 + "time", 4583 + "tracing-subscriber", 4584 + ] 4585 + 4586 + [[package]] 3332 4587 name = "tracing-attributes" 3333 4588 version = "0.1.28" 3334 4589 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3361 4616 ] 3362 4617 3363 4618 [[package]] 4619 + name = "tracing-serde" 4620 + version = "0.2.0" 4621 + source = "registry+https://github.com/rust-lang/crates.io-index" 4622 + checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" 4623 + dependencies = [ 4624 + "serde", 4625 + "tracing-core", 4626 + ] 4627 + 4628 + [[package]] 3364 4629 name = "tracing-subscriber" 3365 4630 version = "0.3.19" 3366 4631 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3370 4635 "nu-ansi-term", 3371 4636 "once_cell", 3372 4637 "regex", 4638 + "serde", 4639 + "serde_json", 3373 4640 "sharded-slab", 3374 4641 "smallvec", 3375 4642 "thread_local", 3376 4643 "tracing", 3377 4644 "tracing-core", 3378 4645 "tracing-log", 4646 + "tracing-serde", 3379 4647 ] 3380 4648 3381 4649 [[package]] ··· 3396 4664 checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 3397 4665 3398 4666 [[package]] 4667 + name = "twoway" 4668 + version = "0.1.8" 4669 + source = "registry+https://github.com/rust-lang/crates.io-index" 4670 + checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" 4671 + dependencies = [ 4672 + "memchr", 4673 + ] 4674 + 4675 + [[package]] 3399 4676 name = "typenum" 3400 4677 version = "1.18.0" 3401 4678 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3406 4683 version = "2.8.1" 3407 4684 source = "registry+https://github.com/rust-lang/crates.io-index" 3408 4685 checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" 4686 + 4687 + [[package]] 4688 + name = "unicode-bidi" 4689 + version = "0.3.18" 4690 + source = "registry+https://github.com/rust-lang/crates.io-index" 4691 + checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" 3409 4692 3410 4693 [[package]] 3411 4694 name = "unicode-ident" ··· 3420 4703 checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" 3421 4704 3422 4705 [[package]] 4706 + name = "unicode-normalization" 4707 + version = "0.1.24" 4708 + source = "registry+https://github.com/rust-lang/crates.io-index" 4709 + checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 4710 + dependencies = [ 4711 + "tinyvec", 4712 + ] 4713 + 4714 + [[package]] 4715 + name = "unicode-properties" 4716 + version = "0.1.3" 4717 + source = "registry+https://github.com/rust-lang/crates.io-index" 4718 + checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" 4719 + 4720 + [[package]] 3423 4721 name = "unicode-width" 3424 4722 version = "0.1.14" 3425 4723 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3444 4742 checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" 3445 4743 3446 4744 [[package]] 4745 + name = "unsynn" 4746 + version = "0.0.26" 4747 + source = "registry+https://github.com/rust-lang/crates.io-index" 4748 + checksum = "14013db4ac11f92d4df4b56edb431eab9c1d9f7aee4791f3205b6a1119b83f54" 4749 + dependencies = [ 4750 + "mutants", 4751 + "proc-macro2", 4752 + "shadow_counted", 4753 + ] 4754 + 4755 + [[package]] 4756 + name = "untrusted" 4757 + version = "0.7.1" 4758 + source = "registry+https://github.com/rust-lang/crates.io-index" 4759 + checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 4760 + 4761 + [[package]] 4762 + name = "untrusted" 4763 + version = "0.9.0" 4764 + source = "registry+https://github.com/rust-lang/crates.io-index" 4765 + checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 4766 + 4767 + [[package]] 3447 4768 name = "url" 3448 4769 version = "2.5.4" 3449 4770 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3473 4794 checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" 3474 4795 dependencies = [ 3475 4796 "getrandom 0.3.3", 4797 + "serde", 3476 4798 ] 3477 4799 3478 4800 [[package]] ··· 3494 4816 checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 3495 4817 3496 4818 [[package]] 4819 + name = "walkdir" 4820 + version = "2.5.0" 4821 + source = "registry+https://github.com/rust-lang/crates.io-index" 4822 + checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 4823 + dependencies = [ 4824 + "same-file", 4825 + "winapi-util", 4826 + ] 4827 + 4828 + [[package]] 3497 4829 name = "want" 3498 4830 version = "0.3.1" 3499 4831 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3516 4848 dependencies = [ 3517 4849 "wit-bindgen-rt", 3518 4850 ] 4851 + 4852 + [[package]] 4853 + name = "wasite" 4854 + version = "0.1.0" 4855 + source = "registry+https://github.com/rust-lang/crates.io-index" 4856 + checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" 3519 4857 3520 4858 [[package]] 3521 4859 name = "wasm-bindgen" ··· 3598 4936 "atrium-oauth", 3599 4937 "atrium-xrpc", 3600 4938 "axum 0.8.4", 4939 + "chrono", 3601 4940 "clap", 4941 + "dashmap", 3602 4942 "diesel", 4943 + "diesel-async", 4944 + "diesel_migrations", 3603 4945 "dotenvy", 4946 + "facet", 4947 + "hyper", 4948 + "jose", 4949 + "jose-jwk", 4950 + "kdl", 4951 + "merde", 4952 + "miette", 4953 + "minijinja", 4954 + "minijinja-contrib", 4955 + "reqwest", 4956 + "serde", 4957 + "serde_json", 4958 + "thiserror 2.0.12", 3604 4959 "tokio", 4960 + "toml", 3605 4961 "tower", 3606 4962 "tower-cookies", 3607 4963 "tower-http", 4964 + "tracing", 4965 + "tracing-appender", 4966 + "tracing-subscriber", 4967 + "uuid", 3608 4968 "weaver-common", 3609 4969 "weaver-workspace-hack", 3610 4970 ] ··· 3624 4984 "hickory-resolver", 3625 4985 "miette", 3626 4986 "n0-future", 4987 + "rouille", 3627 4988 "serde_html_form", 3628 4989 "tokio", 3629 4990 "weaver-common", ··· 3641 5002 "atrium-oauth", 3642 5003 "atrium-xrpc", 3643 5004 "atrium-xrpc-client", 5005 + "compact_string", 3644 5006 "esquema-codegen", 3645 5007 "hickory-resolver", 3646 5008 "http", 5009 + "jose", 5010 + "jose-jwk", 3647 5011 "libsqlite3-sys", 3648 5012 "markdown-weaver", 3649 5013 "merde", ··· 3651 5015 "minijinja", 3652 5016 "n0-future", 3653 5017 "owo-colors", 5018 + "reqwest", 3654 5019 "serde", 3655 5020 "serde_bytes", 3656 5021 "serde_cbor", ··· 3724 5089 ] 3725 5090 3726 5091 [[package]] 5092 + name = "webpki" 5093 + version = "0.22.4" 5094 + source = "registry+https://github.com/rust-lang/crates.io-index" 5095 + checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" 5096 + dependencies = [ 5097 + "ring 0.17.14", 5098 + "untrusted 0.9.0", 5099 + ] 5100 + 5101 + [[package]] 5102 + name = "webpki-roots" 5103 + version = "0.26.11" 5104 + source = "registry+https://github.com/rust-lang/crates.io-index" 5105 + checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" 5106 + dependencies = [ 5107 + "webpki-roots 1.0.0", 5108 + ] 5109 + 5110 + [[package]] 5111 + name = "webpki-roots" 5112 + version = "1.0.0" 5113 + source = "registry+https://github.com/rust-lang/crates.io-index" 5114 + checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" 5115 + dependencies = [ 5116 + "rustls-pki-types", 5117 + ] 5118 + 5119 + [[package]] 5120 + name = "whoami" 5121 + version = "1.6.0" 5122 + source = "registry+https://github.com/rust-lang/crates.io-index" 5123 + checksum = "6994d13118ab492c3c80c1f81928718159254c53c472bf9ce36f8dae4add02a7" 5124 + dependencies = [ 5125 + "redox_syscall", 5126 + "wasite", 5127 + "web-sys", 5128 + ] 5129 + 5130 + [[package]] 3727 5131 name = "widestring" 3728 5132 version = "1.2.0" 3729 5133 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3744 5148 version = "0.4.0" 3745 5149 source = "registry+https://github.com/rust-lang/crates.io-index" 3746 5150 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 5151 + 5152 + [[package]] 5153 + name = "winapi-util" 5154 + version = "0.1.9" 5155 + source = "registry+https://github.com/rust-lang/crates.io-index" 5156 + checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 5157 + dependencies = [ 5158 + "windows-sys 0.59.0", 5159 + ] 3747 5160 3748 5161 [[package]] 3749 5162 name = "winapi-x86_64-pc-windows-gnu" ··· 4064 5477 4065 5478 [[package]] 4066 5479 name = "winnow" 5480 + version = "0.6.24" 5481 + source = "registry+https://github.com/rust-lang/crates.io-index" 5482 + checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" 5483 + dependencies = [ 5484 + "memchr", 5485 + ] 5486 + 5487 + [[package]] 5488 + name = "winnow" 4067 5489 version = "0.7.10" 4068 5490 source = "registry+https://github.com/rust-lang/crates.io-index" 4069 5491 checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" ··· 4087 5509 source = "registry+https://github.com/rust-lang/crates.io-index" 4088 5510 checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 4089 5511 dependencies = [ 4090 - "bitflags", 5512 + "bitflags 2.9.0", 4091 5513 ] 4092 5514 4093 5515 [[package]] ··· 4095 5517 version = "0.6.1" 4096 5518 source = "registry+https://github.com/rust-lang/crates.io-index" 4097 5519 checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" 5520 + 5521 + [[package]] 5522 + name = "yaml-rust" 5523 + version = "0.4.5" 5524 + source = "registry+https://github.com/rust-lang/crates.io-index" 5525 + checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 5526 + dependencies = [ 5527 + "linked-hash-map", 5528 + ] 4098 5529 4099 5530 [[package]] 4100 5531 name = "yaml-rust2" ··· 4178 5609 checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 4179 5610 dependencies = [ 4180 5611 "serde", 5612 + "zeroize_derive", 5613 + ] 5614 + 5615 + [[package]] 5616 + name = "zeroize_derive" 5617 + version = "1.4.2" 5618 + source = "registry+https://github.com/rust-lang/crates.io-index" 5619 + checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" 5620 + dependencies = [ 5621 + "proc-macro2", 5622 + "quote", 5623 + "syn", 4181 5624 ] 4182 5625 4183 5626 [[package]]
+1 -1
Cargo.toml
··· 28 28 29 29 minijinja = { version = "2.9.0", default-features = false } 30 30 minijinja-contrib = { version = "2.9.0", default-features = false } 31 - miette = { version = "7.5.0" } 31 + miette = { version = "7.6" } 32 32 owo-colors = { version = "4.2.0" } 33 33 thiserror = "2.0" 34 34 syntect = "5.2.0"
+45 -8
crates/weaver-appview/Cargo.toml
··· 5 5 license.workspace = true 6 6 publish = false 7 7 8 + [features] 9 + default = [] 10 + embedded-db = ["diesel/sqlite", "diesel-async/sync-connection-wrapper"] 11 + 8 12 [dependencies] 9 - weaver-common = { path = "../weaver-common" } 13 + weaver-common = { path = "../weaver-common", default-features = false } 10 14 weaver-workspace-hack = { version = "0.1", path = "../weaver-workspace-hack" } 11 15 clap = { version = "4.5", features = ["derive", "env", "cargo", "unicode"] } 16 + dotenvy = "0.15" 17 + 18 + tracing = "0.1.40" 19 + tracing-subscriber = { version = "0.3.18", features = ["json", "env-filter"] } 20 + tracing-appender = { version = "0.2.3", features = ["parking_lot"] } 21 + 22 + chrono = "0.4.41" 23 + serde = { version = "1.0.210", features = ["derive"] } 24 + serde_json = "1.0.128" 25 + uuid = { version = "1.8.0", features = ["v7", "serde"] } 26 + merde.workspace = true 12 27 13 28 axum = "0.8" 14 - tower = "0.5" 15 - tower-http = { version = "0.6", features = ["fs"] } 29 + hyper = "1.4.1" 30 + tower = { version = "0.5", features = [] } 16 31 tower-cookies = "0.11" 32 + tower-http = { version = "0.6", features = [ 33 + "fs", 34 + "trace", 35 + "timeout", 36 + "request-id", 37 + "cors", 38 + "normalize-path", 39 + ] } 17 40 41 + reqwest = { version = "0.12.7", default-features = false, features = [ 42 + "json", 43 + "rustls-tls", 44 + ] } 18 45 19 46 tokio = { version = "1.44", features = ["full"] } 20 47 atrium-oauth = "0.1.1" ··· 23 50 atrium-common = "0.1.1" 24 51 atrium-identity = "0.1.3" 25 52 53 + miette = { workspace = true, features = ["fancy", "syntect-highlighter"] } 54 + thiserror = { workspace = true } 26 55 27 - diesel = { version = "2.2.0", features = ["sqlite", "returning_clauses_for_sqlite_3_35"] } 28 - # build libsqlite3 as part of the build process 29 - # uncomment this line if you run into setup issues 30 - # libsqlite3-sys = { version = "0.30", features = ["bundled"] } 31 - dotenvy = "0.15" 56 + diesel = { version = "2.2.0", features = ["postgres", "serde_json","chrono"] } 57 + diesel-async = { version = "0.5.2", features = ["postgres", "deadpool"] } 58 + diesel_migrations = "2.2.0" 59 + facet = { workspace = true } 60 + 61 + kdl = "6.3.4" 62 + toml = { version = "0.8.22", features = ["preserve_order"] } 63 + jose = { version = "0.0.2", features = ["crypto-rustcrypto"] } 64 + jose-jwk = "0.1.2" 65 + 66 + minijinja = { version = "2.9.0" } 67 + minijinja-contrib = { version = "2.9.0" } 68 + dashmap = "6.1.0"
crates/weaver-appview/appview-config.toml

This is a binary file and will not be displayed.

+3
crates/weaver-appview/build.rs
··· 1 + fn main() { 2 + println!("cargo:rerun-if-changed=migrations"); 3 + }
+6
crates/weaver-appview/migrations/00000000000000_diesel_initial_setup/down.sql
··· 1 + -- This file was automatically created by Diesel to setup helper functions 2 + -- and other internal bookkeeping. This file is safe to edit, any future 3 + -- changes will be added to existing projects as new migrations. 4 + 5 + DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); 6 + DROP FUNCTION IF EXISTS diesel_set_updated_at();
+36
crates/weaver-appview/migrations/00000000000000_diesel_initial_setup/up.sql
··· 1 + -- This file was automatically created by Diesel to setup helper functions 2 + -- and other internal bookkeeping. This file is safe to edit, any future 3 + -- changes will be added to existing projects as new migrations. 4 + 5 + 6 + 7 + 8 + -- Sets up a trigger for the given table to automatically set a column called 9 + -- `updated_at` whenever the row is modified (unless `updated_at` was included 10 + -- in the modified columns) 11 + -- 12 + -- # Example 13 + -- 14 + -- ```sql 15 + -- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); 16 + -- 17 + -- SELECT diesel_manage_updated_at('users'); 18 + -- ``` 19 + CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ 20 + BEGIN 21 + EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s 22 + FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); 23 + END; 24 + $$ LANGUAGE plpgsql; 25 + 26 + CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ 27 + BEGIN 28 + IF ( 29 + NEW IS DISTINCT FROM OLD AND 30 + NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at 31 + ) THEN 32 + NEW.updated_at := current_timestamp; 33 + END IF; 34 + RETURN NEW; 35 + END; 36 + $$ LANGUAGE plpgsql;
+20
crates/weaver-appview/migrations/2025-05-15-230036_create_appviewdb/down.sql
··· 1 + -- This file should undo anything in `up.sql` 2 + drop table if exists profile_pronouns; 3 + 4 + drop table if exists profile_links; 5 + 6 + drop table if exists profile; 7 + 8 + drop table if exists emails; 9 + 10 + drop table if exists _jetstream; 11 + 12 + drop table if exists follows; 13 + 14 + drop table if exists public_keys; 15 + 16 + drop table if exists registrations; 17 + 18 + drop table if exists oauth_sessions; 19 + 20 + drop table if exists oauth_requests;
+109
crates/weaver-appview/migrations/2025-05-15-230036_create_appviewdb/up.sql
··· 1 + create table if not exists registrations ( 2 + id serial primary key, 3 + domain text not null unique, 4 + did text not null, 5 + secret text not null, 6 + created timestamp 7 + with 8 + time zone not null default (now () at time zone 'utc'), 9 + registered text 10 + ); 11 + 12 + create table if not exists public_keys ( 13 + id serial primary key, 14 + did text not null, 15 + name text not null, 16 + key_contents text not null, 17 + rkey text not null, 18 + created timestamp 19 + with 20 + time zone not null default (now () at time zone 'utc'), 21 + unique (did, name, key_contents) 22 + ); 23 + 24 + create table if not exists follows ( 25 + user_did text not null, 26 + subject_did text not null, 27 + rkey text not null, 28 + followed_at timestamp 29 + with 30 + time zone not null default (now () at time zone 'utc'), 31 + primary key (user_did, subject_did), 32 + check (user_did <> subject_did) 33 + ); 34 + 35 + create table if not exists _jetstream ( 36 + id serial primary key, 37 + last_time_us integer not null 38 + ); 39 + 40 + create table if not exists emails ( 41 + id serial primary key, 42 + did text not null, 43 + email text not null, 44 + verified integer not null default 0, 45 + verification_code text not null, 46 + last_sent timestamp 47 + with 48 + time zone not null default (now () at time zone 'utc'), 49 + is_primary integer not null default 0, 50 + created timestamp 51 + with 52 + time zone not null default (now () at time zone 'utc'), 53 + unique (did, email) 54 + ); 55 + 56 + create table if not exists profile ( 57 + -- id 58 + id serial primary key, 59 + did text not null, 60 + -- data 61 + avatar text, 62 + description text not null, 63 + include_bluesky boolean not null default false, 64 + include_tangled boolean not null default false, 65 + location text, 66 + pinned_post jsonb, 67 + created_at timestamp 68 + with 69 + time zone default (now () at time zone 'utc'), 70 + -- constraints 71 + unique (did) 72 + ); 73 + 74 + create table if not exists profile_links ( 75 + -- id 76 + id serial primary key, 77 + did text not null, 78 + -- data 79 + link text not null, 80 + -- constraints 81 + foreign key (did) references profile (did) on delete cascade 82 + ); 83 + 84 + create table if not exists profile_pronouns ( 85 + -- id 86 + id serial primary key, 87 + did text not null, 88 + -- data 89 + pronoun text not null, 90 + -- constraints 91 + foreign key (did) references profile (did) on delete cascade 92 + ); 93 + 94 + create table if not exists oauth_requests ( 95 + id serial primary key, 96 + auth_server_iss text not null, 97 + state text, 98 + did text not null, 99 + pkce_verifier text not null, 100 + dpop_key jsonb not null 101 + ); 102 + 103 + create table if not exists oauth_sessions ( 104 + id serial primary key, 105 + did text not null, 106 + pds_url text not null, 107 + session jsonb not null, 108 + expiry text 109 + );
+90
crates/weaver-appview/src/api_error.rs
··· 1 + use axum::{ 2 + Json, 3 + extract::rejection::JsonRejection, 4 + response::{IntoResponse, Response}, 5 + }; 6 + use hyper::StatusCode; 7 + use miette::Diagnostic; 8 + use serde::{Deserialize, Serialize}; 9 + use thiserror::Error; 10 + use tracing::error; 11 + 12 + /// Custom error type for the API. 13 + /// The `#[from]` attribute allows for easy conversion from other error types. 14 + #[derive(Error, Debug, Diagnostic)] 15 + pub enum ApiError { 16 + /// Converts from an Axum built-in extractor error. 17 + #[diagnostic_source] 18 + #[error("Invalid payload.")] 19 + InvalidJsonBody(#[from] JsonRejection), 20 + 21 + /// For errors that occur during manual validation. 22 + #[error("Invalid request: {0}")] 23 + #[diagnostic()] 24 + InvalidRequest(String), 25 + 26 + /// Converts from `sqlx::Error`. 27 + #[error("A database error has occurred.")] 28 + #[diagnostic_source] 29 + DatabaseError(#[from] diesel::result::Error), 30 + 31 + #[error("A Weaver error has occurred.")] 32 + #[diagnostic(transparent)] 33 + WeaverError(#[from] weaver_common::error::Error), 34 + /// Converts from any `anyhow::Error`. 35 + #[error("An internal server error has occurred.")] 36 + #[diagnostic(transparent)] 37 + InternalError(miette::Report), 38 + } 39 + 40 + impl From<miette::Report> for ApiError { 41 + fn from(err: miette::Report) -> Self { 42 + ApiError::InternalError(err) 43 + } 44 + } 45 + 46 + #[derive(Serialize, Deserialize)] 47 + pub struct ApiErrorResp { 48 + pub message: String, 49 + } 50 + 51 + // The IntoResponse implementation for ApiError logs the error message. 52 + // 53 + // To avoid exposing implementation details to API consumers, we separate 54 + // the message that we log from the API response message. 55 + impl IntoResponse for ApiError { 56 + fn into_response(self) -> Response { 57 + // Log detailed error for telemetry. 58 + let error_to_log = match &self { 59 + ApiError::InvalidJsonBody(err) => match err { 60 + JsonRejection::JsonDataError(e) => e.body_text(), 61 + JsonRejection::JsonSyntaxError(e) => e.body_text(), 62 + JsonRejection::MissingJsonContentType(_) => { 63 + "Missing `Content-Type: application/json` header".to_string() 64 + } 65 + JsonRejection::BytesRejection(_) => "Failed to buffer request body".to_string(), 66 + _ => "Unknown error".to_string(), 67 + }, 68 + ApiError::InvalidRequest(_) => format!("{}", self), 69 + ApiError::WeaverError(err) => format!("{}", err), 70 + ApiError::DatabaseError(err) => format!("{}", err), 71 + ApiError::InternalError(err) => format!("{}", err), 72 + }; 73 + error!("{}", error_to_log); 74 + 75 + // Create a generic response to hide specific implementation details. 76 + let resp = ApiErrorResp { 77 + message: self.to_string(), 78 + }; 79 + 80 + // Determine the appropriate status code. 81 + let status = match self { 82 + ApiError::InvalidJsonBody(_) | ApiError::InvalidRequest(_) => StatusCode::BAD_REQUEST, 83 + ApiError::WeaverError(_) | ApiError::DatabaseError(_) | ApiError::InternalError(_) => { 84 + StatusCode::INTERNAL_SERVER_ERROR 85 + } 86 + }; 87 + 88 + (status, Json(resp)).into_response() 89 + } 90 + }
+61
crates/weaver-appview/src/config.rs
··· 1 + use miette::miette; 2 + use serde::{Deserialize, Serialize}; 3 + use std::{env, fs}; 4 + 5 + #[derive(Deserialize, Serialize, Clone)] 6 + pub struct OauthConfig { 7 + pub jwks: Vec<jose_jwk::Jwk>, 8 + } 9 + 10 + #[derive(Deserialize, Serialize, Clone)] 11 + pub struct JetstreamConfig { 12 + pub endpoint: String, 13 + } 14 + 15 + impl Default for JetstreamConfig { 16 + fn default() -> Self { 17 + Self { 18 + endpoint: "wss://jetstream1.us-east.bsky.network/subscribe".into(), 19 + } 20 + } 21 + } 22 + 23 + #[derive(Deserialize, Serialize, Clone)] 24 + pub struct CoreConfig { 25 + pub db_path: String, 26 + pub listen_addr: String, 27 + pub appview_host: String, 28 + pub cookie_secret: String, 29 + } 30 + 31 + impl Default for CoreConfig { 32 + fn default() -> Self { 33 + Self { 34 + db_path: "postgres://postgres:@localhost/weaver_appview".into(), 35 + listen_addr: "0.0.0.0:4000".into(), 36 + appview_host: "https://appview.weaver.sh".into(), 37 + cookie_secret: "00000000000000000000000000000000".into(), 38 + } 39 + } 40 + } 41 + 42 + #[derive(Deserialize, Serialize, Clone)] 43 + pub struct Config { 44 + pub oauth: OauthConfig, 45 + pub jetstream: JetstreamConfig, 46 + pub core: CoreConfig, 47 + } 48 + 49 + impl Config { 50 + pub fn load(config_file: &str) -> miette::Result<Config> { 51 + let mut config_string = fs::read_to_string(config_file) 52 + .map_err(|e| miette!("error reading config file {}", e))?; 53 + // substitute environment variables in config file 54 + for (k, v) in env::vars() { 55 + config_string = config_string.replace(&format!("${}", k), &v); 56 + } 57 + 58 + Ok(toml::from_str(&config_string) 59 + .map_err(|e| miette!("error parsing config file {}", e))?) 60 + } 61 + }
+59
crates/weaver-appview/src/db.rs
··· 1 + use diesel::prelude::*; 2 + use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations}; 3 + pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); 4 + use diesel_async::RunQueryDsl; 5 + use diesel_async::pooled_connection::AsyncDieselConnectionManager; 6 + use diesel_async::pooled_connection::deadpool::Pool; 7 + 8 + #[derive(Clone)] 9 + pub struct Db { 10 + pub pool: Pool<diesel_async::AsyncPgConnection>, 11 + } 12 + 13 + impl Db { 14 + /// Yes, this fuction can and WILL panic if it can't create the connection pool 15 + /// for some reason. We just want to bail because the appview 16 + /// does not work without a database. 17 + pub async fn new(db_path: Option<String>) -> Self { 18 + let database_url = if let Some(db_path) = db_path { 19 + db_path 20 + } else { 21 + std::env::var("DATABASE_URL").expect("DATABASE_URL must be set") 22 + }; 23 + let config = 24 + AsyncDieselConnectionManager::<diesel_async::AsyncPgConnection>::new(database_url); 25 + let pool = Pool::builder(config) 26 + .build() 27 + .expect("Failed to create pool"); 28 + Self { pool } 29 + } 30 + } 31 + 32 + pub fn run_migrations( 33 + db_path: Option<String>, 34 + ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { 35 + let database_url = if let Some(db_path) = db_path { 36 + db_path 37 + } else { 38 + std::env::var("DATABASE_URL").expect("DATABASE_URL must be set") 39 + }; 40 + let mut connection = PgConnection::establish(&database_url) 41 + .unwrap_or_else(|_| panic!("Error connecting to {}", database_url)); 42 + // This will run the necessary migrations. 43 + // 44 + // See the documentation for `MigrationHarness` for 45 + // all available methods. 46 + println!("Attempting migrations..."); 47 + let result = connection.run_pending_migrations(MIGRATIONS); 48 + println!("{:?}", result); 49 + if result.is_err() { 50 + println!("Failed to run migrations"); 51 + return result.map(|_| ()); 52 + } 53 + println!("Migrations Applied:"); 54 + let applied_migrations = connection.applied_migrations()?; 55 + for migration in applied_migrations { 56 + println!(" * {}", migration); 57 + } 58 + Ok(()) 59 + }
+134 -2
crates/weaver-appview/src/main.rs
··· 1 - mod oauth; 1 + pub mod api_error; 2 + 3 + pub mod config; 4 + pub mod db; 5 + pub mod middleware; 6 + pub mod models; 7 + pub mod oauth; 8 + pub mod routes; 9 + pub mod schema; 10 + pub mod state; 11 + pub mod telemetry; 12 + 13 + use axum::Router; 14 + use clap::Parser; 15 + use config::*; 16 + use db::*; 17 + use diesel::prelude::*; 18 + use diesel_async::{AsyncConnection, AsyncPgConnection, RunQueryDsl}; 19 + use dotenvy::dotenv; 20 + use miette::IntoDiagnostic; 21 + use miette::miette; 22 + use state::*; 23 + use std::env; 24 + 25 + use tokio::net::TcpListener; 26 + use tracing::{debug, error, info}; 27 + 28 + #[derive(Parser)] 29 + #[command(author, version, about, long_about = None)] 30 + struct Cli { 31 + #[arg( 32 + short, 33 + long, 34 + value_name = "FILE", 35 + default_value = "appview-config.toml" 36 + )] 37 + config: String, 38 + } 2 39 3 40 #[tokio::main] 4 - async fn main() {} 41 + async fn main() -> miette::Result<()> { 42 + let config = initialize()?; 43 + // Run any migrations before we do anything else. 44 + let db_path = config.core.db_path.clone(); 45 + let _ = tokio::task::spawn_blocking(|| db::run_migrations(Some(db_path))) 46 + .await 47 + .into_diagnostic()?; 48 + let db = Db::new(Some(config.core.db_path.clone())).await; 49 + debug!("Connected to database"); 50 + // Spin up our server. 51 + info!("Starting server on {}", config.core.listen_addr); 52 + let listener = TcpListener::bind(&config.core.listen_addr) 53 + .await 54 + .expect("Failed to bind address"); 55 + let router = router(config, db); 56 + axum::serve(listener, router) 57 + .await 58 + .expect("Failed to start server"); 59 + Ok(()) 60 + } 61 + 62 + pub fn router(cfg: Config, db: Db) -> Router { 63 + let app_state = AppState::new(cfg, db); 64 + 65 + // Middleware that adds high level tracing to a Service. 66 + // Trace comes with good defaults but also supports customizing many aspects of the output: 67 + // https://docs.rs/tower-http/latest/tower_http/trace/index.html 68 + let trace_layer = telemetry::trace_layer(); 69 + 70 + // Sets 'x-request-id' header with randomly generated uuid v7. 71 + let request_id_layer = middleware::request_id_layer(); 72 + 73 + // Propagates 'x-request-id' header from the request to the response. 74 + let propagate_request_id_layer = middleware::propagate_request_id_layer(); 75 + 76 + // Layer that applies the Cors middleware which adds headers for CORS. 77 + let cors_layer = middleware::cors_layer(); 78 + 79 + // Layer that applies the Timeout middleware, which sets a timeout for requests. 80 + // The default value is 15 seconds. 81 + let timeout_layer = middleware::timeout_layer(); 82 + 83 + // Any trailing slashes from request paths will be removed. For example, a request with `/foo/` 84 + // will be changed to `/foo` before reaching the internal service. 85 + let normalize_path_layer = middleware::normalize_path_layer(); 86 + 87 + // Create the router with the routes. 88 + let router = routes::router(); 89 + 90 + // Combine all the routes and apply the middleware layers. 91 + // The order of the layers is important. The first layer is the outermost layer. 92 + Router::new() 93 + .merge(router) 94 + .layer(normalize_path_layer) 95 + .layer(cors_layer) 96 + .layer(timeout_layer) 97 + .layer(propagate_request_id_layer) 98 + .layer(trace_layer) 99 + .layer(request_id_layer) 100 + .with_state(app_state) 101 + } 102 + 103 + pub fn initialize() -> miette::Result<Config> { 104 + miette::set_hook(Box::new(|_| { 105 + Box::new( 106 + miette::MietteHandlerOpts::new() 107 + .terminal_links(true) 108 + //.rgb_colors(miette::RgbColors::) 109 + .with_cause_chain() 110 + .with_syntax_highlighting(miette::highlighters::SyntectHighlighter::default()) 111 + .color(true) 112 + .context_lines(5) 113 + .tab_width(2) 114 + .break_words(true) 115 + .build(), 116 + ) 117 + })) 118 + .map_err(|e| miette!("Failed to set miette hook: {}", e))?; 119 + miette::set_panic_hook(); 120 + dotenv().ok(); 121 + let cli = Cli::parse(); 122 + let config = config::Config::load(&cli.config); 123 + let config = if let Err(e) = config { 124 + error!("{}", e); 125 + config::Config::load( 126 + &env::var("APPVIEW_CONFIG").expect("Either set APPVIEW_CONFIG to the path to your config file, pass --config FILE to specify the path, or create a file called appview-config.toml in the directory where you are running the binary from."), 127 + ) 128 + .map_err(|e| miette!(e)) 129 + } else { 130 + config 131 + }?; 132 + let log_dir = env::var("LOG_DIR").unwrap_or_else(|_| "/tmp/appview".to_string()); 133 + std::fs::create_dir_all(&log_dir).unwrap(); 134 + let _guard = telemetry::setup_tracing(&log_dir); 135 + Ok(config) 136 + }
+61
crates/weaver-appview/src/middleware.rs
··· 1 + use std::time::Duration; 2 + 3 + use axum::http::HeaderName; 4 + use hyper::Request; 5 + use tower_http::{ 6 + cors::{AllowHeaders, Any, CorsLayer}, 7 + normalize_path::NormalizePathLayer, 8 + request_id::{MakeRequestId, PropagateRequestIdLayer, RequestId, SetRequestIdLayer}, 9 + timeout::TimeoutLayer, 10 + }; 11 + 12 + #[derive(Clone, Default)] 13 + pub struct Id; 14 + 15 + impl MakeRequestId for Id { 16 + fn make_request_id<B>(&mut self, _: &Request<B>) -> Option<RequestId> { 17 + let id = uuid::Uuid::now_v7().to_string().parse().unwrap(); 18 + Some(RequestId::new(id)) 19 + } 20 + } 21 + 22 + /// Sets the 'x-request-id' header with a randomly generated UUID v7. 23 + /// 24 + /// SetRequestId will not override request IDs if they are already present 25 + /// on requests or responses. 26 + pub fn request_id_layer() -> SetRequestIdLayer<Id> { 27 + let x_request_id = HeaderName::from_static("x-request-id"); 28 + SetRequestIdLayer::new(x_request_id.clone(), Id) 29 + } 30 + 31 + // Propagates 'x-request-id' header from the request to the response. 32 + /// 33 + /// PropagateRequestId wont override request ids if its already 34 + /// present on requests or responses. 35 + pub fn propagate_request_id_layer() -> PropagateRequestIdLayer { 36 + let x_request_id = HeaderName::from_static("x-request-id"); 37 + PropagateRequestIdLayer::new(x_request_id) 38 + } 39 + 40 + /// Layer that applies the Cors middleware which adds headers for CORS. 41 + pub fn cors_layer() -> CorsLayer { 42 + CorsLayer::new() 43 + .allow_origin(Any) 44 + .allow_methods(Any) 45 + .allow_headers(AllowHeaders::mirror_request()) 46 + .max_age(Duration::from_secs(600)) 47 + } 48 + 49 + /// Layer that applies the Timeout middleware which apply a timeout to requests. 50 + /// The default timeout value is set to 15 seconds. 51 + pub fn timeout_layer() -> TimeoutLayer { 52 + TimeoutLayer::new(Duration::from_secs(15)) 53 + } 54 + 55 + /// Middleware that normalizes paths. 56 + /// 57 + /// Any trailing slashes from request paths will be removed. For example, a request with `/foo/` 58 + /// will be changed to `/foo` before reaching the inner service. 59 + pub fn normalize_path_layer() -> NormalizePathLayer { 60 + NormalizePathLayer::trim_trailing_slash() 61 + }
+104
crates/weaver-appview/src/models.rs
··· 1 + use chrono::{DateTime, Utc}; 2 + use diesel::prelude::*; 3 + 4 + #[derive(Queryable, Selectable)] 5 + #[diesel(table_name = crate::schema::profile)] 6 + pub struct Profile { 7 + pub id: i32, 8 + pub did: String, 9 + pub avatar: Option<String>, 10 + pub description: String, 11 + pub include_bluesky: bool, 12 + pub include_tangled: bool, 13 + pub location: Option<String>, 14 + pub pinned_post: Option<serde_json::Value>, 15 + pub created_at: Option<DateTime<Utc>>, 16 + } 17 + 18 + #[derive(Queryable, Selectable)] 19 + #[diesel(table_name = crate::schema::registrations)] 20 + pub struct Registration { 21 + pub id: i32, 22 + pub domain: String, 23 + pub did: String, 24 + pub secret: String, 25 + pub created: DateTime<Utc>, 26 + pub registered: Option<String>, 27 + } 28 + 29 + #[derive(Queryable, Selectable)] 30 + #[diesel(table_name = crate::schema::public_keys)] 31 + pub struct PublicKey { 32 + pub id: i32, 33 + pub did: String, 34 + pub name: String, 35 + pub key_contents: String, 36 + pub rkey: String, 37 + pub created: DateTime<Utc>, 38 + } 39 + 40 + #[derive(Queryable, Selectable)] 41 + #[diesel(table_name = crate::schema::follows)] 42 + pub struct Follow { 43 + pub user_did: String, 44 + pub subject_did: String, 45 + pub rkey: String, 46 + pub followed_at: DateTime<Utc>, 47 + } 48 + 49 + #[derive(Queryable, Selectable)] 50 + #[diesel(table_name = crate::schema::_jetstream)] 51 + pub struct Jetstream { 52 + pub id: i32, 53 + pub last_time_us: i32, 54 + } 55 + 56 + #[derive(Queryable, Selectable)] 57 + #[diesel(table_name = crate::schema::emails)] 58 + pub struct Email { 59 + pub id: i32, 60 + pub did: String, 61 + pub email: String, 62 + pub verified: i32, 63 + pub verification_code: String, 64 + pub last_sent: DateTime<Utc>, 65 + pub is_primary: i32, 66 + pub created: DateTime<Utc>, 67 + } 68 + 69 + #[derive(Queryable, Selectable)] 70 + #[diesel(table_name = crate::schema::profile_links)] 71 + pub struct ProfileLink { 72 + pub id: i32, 73 + pub did: String, 74 + pub link: String, 75 + } 76 + 77 + #[derive(Queryable, Selectable)] 78 + #[diesel(table_name = crate::schema::profile_pronouns)] 79 + pub struct ProfilePronoun { 80 + pub id: i32, 81 + pub did: String, 82 + pub pronoun: String, 83 + } 84 + 85 + #[derive(Queryable, Selectable)] 86 + #[diesel(table_name = crate::schema::oauth_requests)] 87 + pub struct OauthRequest { 88 + pub id: i32, 89 + pub auth_server_iss: String, 90 + pub state: Option<String>, 91 + pub did: String, 92 + pub pkce_verifier: String, 93 + pub dpop_key: serde_json::Value, 94 + } 95 + 96 + #[derive(Queryable, Selectable, Insertable)] 97 + #[diesel(table_name = crate::schema::oauth_sessions)] 98 + pub struct OauthSession { 99 + pub id: i32, 100 + pub did: String, 101 + pub pds_url: String, 102 + pub session: serde_json::Value, 103 + pub expiry: Option<String>, 104 + }
+176
crates/weaver-appview/src/oauth.rs
··· 1 + use atrium_api::types::string::Did; 2 + use atrium_identity::did::CommonDidResolver; 3 + use atrium_identity::handle::AtprotoHandleResolver; 4 + use atrium_oauth::DefaultHttpClient; 5 + use atrium_oauth::store::session::Session; 6 + use atrium_oauth::store::state::InternalStateData; 7 + use diesel::prelude::*; 8 + use diesel_async::RunQueryDsl; 9 + use jose_jwk::Key; 10 + use miette::IntoDiagnostic; 11 + use miette::Result; 12 + use miette::miette; 13 + use weaver_common::client::WeaverHttpClient; 14 + use weaver_common::oauth::WeaverOAuthClient; 15 + use weaver_common::resolver::HickoryDnsTxtResolver; 16 + 17 + use crate::api_error::ApiError; 18 + use crate::models::OauthRequest; 19 + use crate::models::OauthSession; 20 + 21 + /// Basic implementation of the atrium-common Store trait for a database using diesel-async and deadpool to store OAuth sessions. 22 + pub struct DBSessionStore { 23 + pub db: crate::db::Db, 24 + } 25 + 26 + impl DBSessionStore { 27 + pub fn new(db: &crate::db::Db) -> Self { 28 + Self { db: db.clone() } 29 + } 30 + 31 + pub async fn get(&self, user_did: &Did) -> Result<Option<Session>> { 32 + use crate::schema::oauth_sessions::dsl::*; 33 + let mut conn = self.db.pool.get().await.into_diagnostic()?; 34 + 35 + let results: Vec<OauthSession> = oauth_sessions 36 + .filter(did.eq(user_did.as_str())) 37 + .limit(1) 38 + .select(OauthSession::as_select()) 39 + .load(&mut conn) 40 + .await 41 + .into_diagnostic()?; 42 + 43 + if let Some(sess) = results.get(0) { 44 + let sess: Option<Session> = serde_json::from_value(sess.session.clone()).ok(); 45 + Ok(sess) 46 + } else { 47 + Ok(None) 48 + } 49 + } 50 + 51 + async fn set(&self, key: Did, value: Session) -> Result<()> { 52 + use crate::schema::oauth_sessions::dsl::*; 53 + let mut conn = self.db.pool.get().await.into_diagnostic()?; 54 + // do an upsert or similar here? 55 + let sess = OauthSession { 56 + id: 0, 57 + did: key.as_str().to_string(), 58 + pds_url: value.token_set.aud.clone(), 59 + session: serde_json::to_value(&value).unwrap(), 60 + expiry: value.token_set.expires_at.map(|t| t.as_str().to_string()), 61 + }; 62 + 63 + diesel::insert_into(oauth_sessions) 64 + .values(&sess) 65 + .execute(&mut conn) 66 + .await 67 + .into_diagnostic()?; 68 + 69 + Ok(()) 70 + } 71 + 72 + async fn del(&self, key: &Did) -> Result<()> { 73 + use crate::schema::oauth_sessions::dsl::*; 74 + let mut conn = self.db.pool.get().await.into_diagnostic()?; 75 + let query = diesel::delete(oauth_sessions.filter(did.eq(key.as_str()))); 76 + query.execute(&mut conn).await.into_diagnostic()?; 77 + Ok(()) 78 + } 79 + 80 + async fn clear(&self) -> Result<()> { 81 + Err(miette!( 82 + "this would clear the whole fucking table in the database so nope!" 83 + )) 84 + } 85 + } 86 + 87 + impl atrium_common::store::Store<Did, Session> for DBSessionStore { 88 + type Error = ApiError; 89 + 90 + async fn get(&self, did: &Did) -> Result<Option<Session>, Self::Error> { 91 + Ok(self.get(did).await?) 92 + } 93 + 94 + async fn set(&self, key: Did, value: Session) -> Result<(), Self::Error> { 95 + Ok(self.set(key, value).await?) 96 + } 97 + 98 + async fn del(&self, key: &Did) -> Result<(), Self::Error> { 99 + Ok(self.del(key).await?) 100 + } 101 + 102 + async fn clear(&self) -> Result<(), Self::Error> { 103 + Ok(self.clear().await?) 104 + } 105 + } 106 + 107 + impl atrium_oauth::store::session::SessionStore for DBSessionStore {} 108 + 109 + pub struct DBStateStore { 110 + pub db: crate::db::Db, 111 + } 112 + 113 + impl DBStateStore { 114 + pub fn new(db: &crate::db::Db) -> Self { 115 + Self { db: db.clone() } 116 + } 117 + 118 + pub async fn get(&self, key: &str) -> Result<Option<InternalStateData>> { 119 + use crate::schema::oauth_requests::dsl::*; 120 + let mut conn = self.db.pool.get().await.into_diagnostic()?; 121 + 122 + let results: Vec<OauthRequest> = oauth_requests 123 + .filter(state.eq(key)) 124 + .limit(1) 125 + .select(OauthRequest::as_select()) 126 + .load(&mut conn) 127 + .await 128 + .into_diagnostic()?; 129 + 130 + if let Some(req) = results.get(0) { 131 + if let Ok(key) = serde_json::from_value::<Key>(req.dpop_key.clone()) { 132 + Ok(Some(InternalStateData { 133 + iss: req.auth_server_iss.clone(), 134 + dpop_key: key, 135 + verifier: req.pkce_verifier.clone(), 136 + app_state: req.state.clone(), 137 + })) 138 + } else { 139 + Ok(None) 140 + } 141 + } else { 142 + Ok(None) 143 + } 144 + } 145 + } 146 + 147 + impl atrium_common::store::Store<String, InternalStateData> for DBStateStore { 148 + type Error = ApiError; 149 + 150 + async fn get(&self, key: &String) -> Result<Option<InternalStateData>, Self::Error> { 151 + Ok(self.get(key).await?) 152 + } 153 + 154 + async fn set(&self, key: String, value: InternalStateData) -> Result<(), Self::Error> { 155 + todo!() 156 + } 157 + 158 + async fn del(&self, key: &String) -> Result<(), Self::Error> { 159 + todo!() 160 + } 161 + 162 + async fn clear(&self) -> Result<(), Self::Error> { 163 + todo!() 164 + } 165 + } 166 + 167 + impl atrium_oauth::store::state::StateStore for DBStateStore {} 168 + 169 + pub type AppviewOAuthSession = atrium_oauth::OAuthSession< 170 + DefaultHttpClient, 171 + CommonDidResolver<WeaverHttpClient>, 172 + AtprotoHandleResolver<HickoryDnsTxtResolver, WeaverHttpClient>, 173 + DBSessionStore, 174 + >; 175 + 176 + pub type AppviewOAuthClient = WeaverOAuthClient<DBStateStore, DBSessionStore>;
+8
crates/weaver-appview/src/routes/health_check.rs
··· 1 + use axum::Json; 2 + use serde_json::{Value, json}; 3 + 4 + use crate::api_error::ApiError; 5 + 6 + pub async fn health_check() -> Result<Json<Value>, ApiError> { 7 + Ok(Json(json!({ "status": "ok" }))) 8 + }
+4
crates/weaver-appview/src/routes/login.rs
··· 1 + use axum::Json; 2 + use serde_json::{Value, json}; 3 + 4 + use crate::api_error::ApiError;
+4
crates/weaver-appview/src/routes/logout.rs
··· 1 + use axum::Json; 2 + use serde_json::{Value, json}; 3 + 4 + use crate::api_error::ApiError;
+10
crates/weaver-appview/src/routes/mod.rs
··· 1 + use axum::{Router, routing::get}; 2 + 3 + pub mod health_check; 4 + pub mod oauth; 5 + 6 + use crate::AppState; 7 + 8 + pub fn router() -> Router<AppState> { 9 + Router::new().route("/health_check", get(health_check::health_check)) 10 + }
+78
crates/weaver-appview/src/routes/oauth.rs
··· 1 + use atrium_oauth::{AuthorizeOptions, CallbackParams}; 2 + use axum::{ 3 + Form, Json, 4 + extract::{Query, State}, 5 + }; 6 + use hyper::StatusCode; 7 + use miette::{IntoDiagnostic, Result, miette}; 8 + use serde_json::{Value, json}; 9 + 10 + use crate::{api_error::ApiError, state::AppState}; 11 + 12 + /// Passthrough callback for native endpoint 13 + pub async fn callback_native( 14 + Query(params): Query<CallbackParams>, 15 + ) -> Result<Json<Value>, ApiError> { 16 + Ok(Json(json! ({ 17 + "code": params.code, 18 + "iss": params.iss, 19 + "state": params.state, 20 + }))) 21 + } 22 + 23 + pub async fn callback( 24 + State(state): State<AppState>, 25 + Query(params): Query<CallbackParams>, 26 + ) -> Result<Json<Value>, ApiError> { 27 + let oauth_client = state.oauth_client(); 28 + let active_sessions = state.active_sessions(); 29 + let (session, state) = oauth_client 30 + .callback(params) 31 + .await 32 + .expect("oauth callback failed"); 33 + if let Some(ref state) = state { 34 + active_sessions.insert(state.clone(), session); 35 + } 36 + Ok(Json(json!({ 37 + "status": "authenticated", 38 + "state": state 39 + }))) 40 + } 41 + 42 + pub async fn get_client_metadata(State(state): State<AppState>) -> Result<Json<Value>, ApiError> { 43 + let client_metadata = &state.oauth_client().client_metadata; 44 + 45 + Ok(Json(serde_json::to_value(client_metadata).map_err( 46 + |e| ApiError::InternalError(miette!("json serialization error: {e}")), 47 + )?)) 48 + } 49 + 50 + pub async fn get_jwks(State(state): State<AppState>) -> Result<Json<Value>, ApiError> { 51 + let jwks = state.oauth_client().jwks(); 52 + 53 + Ok(Json(serde_json::to_value(jwks).map_err(|e| { 54 + ApiError::InternalError(miette!("json serialization error: {e}")) 55 + })?)) 56 + } 57 + 58 + pub async fn login(State(state): State<AppState>) -> Result<Json<Value>, ApiError> { 59 + Ok(Json(json!({ "status": "ok" }))) 60 + } 61 + 62 + pub struct AuthorizeParams(String, AuthorizeOptions); 63 + 64 + pub async fn authorize( 65 + State(state): State<AppState>, 66 + Form(AuthorizeParams(endpoint, options)): Form<AuthorizeParams>, 67 + ) -> Result<Json<Value>, ApiError> { 68 + let url = state 69 + .oauth_client() 70 + .authorize(endpoint, options) 71 + .await 72 + .map_err(|e| ApiError::InternalError(miette!("oauth authorize error: {e}")))?; 73 + Ok(Json(json!({ "url": url }))) 74 + } 75 + 76 + pub async fn logout(State(state): State<AppState>) -> Result<Json<Value>, ApiError> { 77 + Ok(Json(json!({ "status": "ok" }))) 78 + }
+116
crates/weaver-appview/src/schema.rs
··· 1 + // @generated automatically by Diesel CLI. 2 + 3 + diesel::table! { 4 + _jetstream (id) { 5 + id -> Int4, 6 + last_time_us -> Int4, 7 + } 8 + } 9 + 10 + diesel::table! { 11 + emails (id) { 12 + id -> Int4, 13 + did -> Text, 14 + email -> Text, 15 + verified -> Int4, 16 + verification_code -> Text, 17 + last_sent -> Timestamptz, 18 + is_primary -> Int4, 19 + created -> Timestamptz, 20 + } 21 + } 22 + 23 + diesel::table! { 24 + follows (user_did, subject_did) { 25 + user_did -> Text, 26 + subject_did -> Text, 27 + rkey -> Text, 28 + followed_at -> Timestamptz, 29 + } 30 + } 31 + 32 + diesel::table! { 33 + oauth_requests (id) { 34 + id -> Int4, 35 + auth_server_iss -> Text, 36 + state -> Nullable<Text>, 37 + did -> Text, 38 + pkce_verifier -> Text, 39 + dpop_key -> Jsonb, 40 + } 41 + } 42 + 43 + diesel::table! { 44 + oauth_sessions (id) { 45 + id -> Int4, 46 + did -> Text, 47 + pds_url -> Text, 48 + session -> Jsonb, 49 + expiry -> Nullable<Text>, 50 + } 51 + } 52 + 53 + diesel::table! { 54 + profile (id) { 55 + id -> Int4, 56 + did -> Text, 57 + avatar -> Nullable<Text>, 58 + description -> Text, 59 + include_bluesky -> Bool, 60 + include_tangled -> Bool, 61 + location -> Nullable<Text>, 62 + pinned_post -> Nullable<Jsonb>, 63 + created_at -> Nullable<Timestamptz>, 64 + } 65 + } 66 + 67 + diesel::table! { 68 + profile_links (id) { 69 + id -> Int4, 70 + did -> Text, 71 + link -> Text, 72 + } 73 + } 74 + 75 + diesel::table! { 76 + profile_pronouns (id) { 77 + id -> Int4, 78 + did -> Text, 79 + pronoun -> Text, 80 + } 81 + } 82 + 83 + diesel::table! { 84 + public_keys (id) { 85 + id -> Int4, 86 + did -> Text, 87 + name -> Text, 88 + key_contents -> Text, 89 + rkey -> Text, 90 + created -> Timestamptz, 91 + } 92 + } 93 + 94 + diesel::table! { 95 + registrations (id) { 96 + id -> Int4, 97 + domain -> Text, 98 + did -> Text, 99 + secret -> Text, 100 + created -> Timestamptz, 101 + registered -> Nullable<Text>, 102 + } 103 + } 104 + 105 + diesel::allow_tables_to_appear_in_same_query!( 106 + _jetstream, 107 + emails, 108 + follows, 109 + oauth_requests, 110 + oauth_sessions, 111 + profile, 112 + profile_links, 113 + profile_pronouns, 114 + public_keys, 115 + registrations, 116 + );
+51
crates/weaver-appview/src/state.rs
··· 1 + use crate::oauth::AppviewOAuthClient; 2 + use dashmap::DashMap; 3 + 4 + use crate::config::Config; 5 + use crate::db::Db; 6 + use crate::oauth::{AppviewOAuthSession, DBSessionStore, DBStateStore}; 7 + use std::sync::Arc; 8 + 9 + pub struct AppStateInner { 10 + pub cfg: Config, 11 + pub oauth_client: AppviewOAuthClient, 12 + pub active_sessions: DashMap<String, AppviewOAuthSession>, 13 + } 14 + 15 + #[derive(Clone)] 16 + pub struct AppState { 17 + pub db: Db, 18 + inner: Arc<AppStateInner>, 19 + } 20 + 21 + impl AppState { 22 + pub fn new(cfg: Config, db: Db) -> Self { 23 + let oauth_client = weaver_common::oauth::oauth_client( 24 + &cfg.core.appview_host, 25 + Some(cfg.oauth.jwks.clone()), 26 + DBSessionStore::new(&db), 27 + DBStateStore::new(&db), 28 + ) 29 + .unwrap(); 30 + Self { 31 + db, 32 + inner: Arc::new(AppStateInner { 33 + cfg, 34 + oauth_client, 35 + active_sessions: DashMap::new(), 36 + }), 37 + } 38 + } 39 + 40 + pub fn cfg(&self) -> &Config { 41 + &self.inner.as_ref().cfg 42 + } 43 + 44 + pub fn oauth_client(&self) -> &AppviewOAuthClient { 45 + &self.inner.as_ref().oauth_client 46 + } 47 + 48 + pub fn active_sessions(&self) -> &DashMap<String, AppviewOAuthSession> { 49 + &self.inner.as_ref().active_sessions 50 + } 51 + }
+48
crates/weaver-appview/src/telemetry.rs
··· 1 + use tower_http::{ 2 + classify::{ServerErrorsAsFailures, SharedClassifier}, 3 + trace::{DefaultMakeSpan, DefaultOnRequest, DefaultOnResponse, TraceLayer}, 4 + }; 5 + use tracing::Level; 6 + use tracing_appender::{self, non_blocking, non_blocking::WorkerGuard, rolling::daily}; 7 + use tracing_subscriber::{ 8 + EnvFilter, 9 + fmt::{self, layer, writer::MakeWriterExt}, 10 + layer::SubscriberExt, 11 + registry, 12 + util::SubscriberInitExt, 13 + }; 14 + /// The `EnvFilter` type is used to filter log events based on the value of an environment variable. 15 + /// In this case, we are using the `try_from_default_env` method to attempt to read the `RUST_LOG` environment variable, 16 + /// which is used to set the log level for the application. 17 + /// If the environment variable is not set, we default to the log level of `debug`. 18 + /// The `RUST_LOG` environment variable is set in the Dockerfile and .env files. 19 + pub fn setup_tracing<S: AsRef<str>>(logdir: S) -> WorkerGuard { 20 + let (non_blocking_appender, guard) = non_blocking(daily(logdir.as_ref(), "general.log")); 21 + let env_filter_layer = EnvFilter::try_from_default_env().unwrap_or_else(|_| { 22 + format!( 23 + "debug,{}=debug,tower_http=debug,axum=debug,hyper=debug,axum::rejection=trace,markdown=info", 24 + env!("CARGO_PKG_NAME"), 25 + ).into() 26 + }); 27 + let formatting_layer = fmt::layer().json(); 28 + tracing_subscriber::registry() 29 + .with(env_filter_layer) 30 + .with(formatting_layer) 31 + .with( 32 + layer() 33 + .with_writer(std::io::stdout.with_max_level(Level::DEBUG)) 34 + .event_format(tracing_subscriber::fmt::format().pretty()), 35 + ) 36 + .with(layer().with_writer(non_blocking_appender.with_max_level(Level::INFO))) 37 + .init(); 38 + guard 39 + } 40 + 41 + /// Returns a `TraceLayer` for HTTP requests and responses. 42 + /// The `TraceLayer` is used to trace requests and responses in the application. 43 + pub fn trace_layer() -> TraceLayer<SharedClassifier<ServerErrorsAsFailures>> { 44 + TraceLayer::new_for_http() 45 + .make_span_with(DefaultMakeSpan::new().level(Level::INFO)) 46 + .on_request(DefaultOnRequest::new().level(Level::INFO)) 47 + .on_response(DefaultOnResponse::new().level(Level::INFO)) 48 + }
+1
crates/weaver-cli/Cargo.toml
··· 26 26 27 27 # temp for testing 28 28 tokio = { version = "1.45.0", features = ["full"] } 29 + rouille = { version = "3.6.2", features = ["rustls"] }
+36 -14
crates/weaver-cli/src/main.rs
··· 1 1 use atrium_api::agent::Agent; 2 - use atrium_api::xrpc::http::Uri; 3 2 use atrium_oauth::AuthorizeOptions; 3 + use atrium_oauth::CallbackParams; 4 4 use atrium_oauth::KnownScope; 5 5 use atrium_oauth::Scope; 6 - use std::{ 7 - error, 8 - io::{BufRead, Write, stdin, stdout}, 9 - }; 6 + use rouille::Server; 7 + use std::error; 8 + use tokio::sync::mpsc; 10 9 11 10 #[tokio::main] 12 11 async fn main() -> Result<(), Box<dyn error::Error>> { 13 - let client = weaver_common::oauth::default_oauth_client("https://appview.weaver.sh")?; 12 + let (tx, mut rx) = mpsc::channel(5); 13 + let server = Server::new("0.0.0.0:4000", move |request| { 14 + create_callback_router(request, tx.clone()) 15 + }) 16 + .expect("Could not start server"); 17 + let (server_handle, server_stop) = server.stoppable(); 18 + let client = weaver_common::oauth::default_native_oauth_client()?; 14 19 println!( 15 - "Authorization url: {}", 20 + "To authenticate with your PDS, visit:\r\n\t {}", 16 21 client 17 22 .authorize( 18 23 std::env::var("HANDLE").unwrap_or(String::from("https://atproto.systems")), ··· 27 32 .await? 28 33 ); 29 34 30 - print!("Redirected url: "); 31 - stdout().lock().flush()?; 32 - let mut url = String::new(); 33 - stdin().lock().read_line(&mut url)?; 34 - 35 - let uri = url.trim().parse::<Uri>()?; 36 - let params = serde_html_form::from_str(uri.query().unwrap())?; 35 + let params = rx.recv().await.unwrap(); 37 36 let (session, _) = client.callback(params).await?; 37 + server_stop.send(()).expect("Failed to stop callbackserver"); 38 38 let agent = Agent::new(session); 39 39 let output = agent 40 40 .api ··· 53 53 for feed in &output.feed { 54 54 println!("{feed:?}"); 55 55 } 56 + server_handle.join().unwrap(); 56 57 Ok(()) 57 58 } 59 + 60 + pub fn create_callback_router( 61 + request: &rouille::Request, 62 + tx: mpsc::Sender<CallbackParams>, 63 + ) -> rouille::Response { 64 + rouille::router!(request, 65 + (GET) (/oauth/callback) => { 66 + let state = request.get_param("state").unwrap(); 67 + let code = request.get_param("code").unwrap(); 68 + let iss = request.get_param("iss").unwrap(); 69 + let callback_params = CallbackParams { 70 + state: Some(state), 71 + code, 72 + iss: Some(iss), 73 + }; 74 + tx.try_send(callback_params).unwrap(); 75 + rouille::Response::text("Logged in!") 76 + }, 77 + _ => rouille::Response::empty_404() 78 + ) 79 + }
+5 -1
crates/weaver-common/Cargo.toml
··· 25 25 atrium-xrpc-client = "0.5.13" 26 26 atrium-lex = { workspace = true } 27 27 28 - merde = { workspace = true, features = ["yaml"] } 28 + merde = { workspace = true, features = ["yaml", "serde"] } 29 29 serde = { workspace = true } 30 30 serde_json = { version = "1.0.140", features = ["preserve_order", "raw_value"] } 31 31 serde_ipld_dagcbor = { version = "0.6.1", features = ["codec"] } ··· 51 51 tracing = { workspace = true } 52 52 hickory-resolver = "0.24.1" 53 53 toml = "0.8.22" 54 + compact_string = "0.1.0" 55 + jose = { version = "0.0.2", features = ["crypto-rustcrypto"] } 56 + jose-jwk = "0.1.2" 57 + reqwest = "0.12.15" 54 58 55 59 56 60
+67
crates/weaver-common/src/client.rs
··· 1 + use atrium_api::app::bsky::actor::defs::ProfileViewDetailedData; 2 + use atrium_api::{agent::SessionManager, types::string::AtIdentifier}; 3 + 4 + use crate::Error; 5 + 6 + pub struct WeaverHttpClient { 7 + pub client: reqwest::Client, 8 + } 9 + 10 + impl atrium_xrpc::HttpClient for WeaverHttpClient { 11 + async fn send_http( 12 + &self, 13 + request: atrium_xrpc::http::Request<Vec<u8>>, 14 + ) -> core::result::Result< 15 + atrium_xrpc::http::Response<Vec<u8>>, 16 + Box<dyn std::error::Error + Send + Sync + 'static>, 17 + > { 18 + let response = self.client.execute(request.try_into()?).await?; 19 + let mut builder = atrium_xrpc::http::Response::builder().status(response.status()); 20 + for (k, v) in response.headers() { 21 + builder = builder.header(k, v); 22 + } 23 + builder 24 + .body(response.bytes().await?.to_vec()) 25 + .map_err(Into::into) 26 + } 27 + } 28 + 29 + impl Default for WeaverHttpClient { 30 + fn default() -> Self { 31 + Self { 32 + client: reqwest::Client::new(), 33 + } 34 + } 35 + } 36 + 37 + pub struct WeaverAgent<M> 38 + where 39 + M: SessionManager + Send + Sync, 40 + { 41 + pub a: atrium_api::agent::Agent<M>, 42 + } 43 + 44 + impl<M> WeaverAgent<M> 45 + where 46 + M: SessionManager + Send + Sync, 47 + { 48 + pub fn new(session_manager: M) -> Self { 49 + Self { 50 + a: atrium_api::agent::Agent::new(session_manager), 51 + } 52 + } 53 + 54 + pub async fn get_profile(&self, actor: AtIdentifier) -> Result<ProfileViewDetailedData, Error> { 55 + use atrium_api::app::bsky::actor::get_profile::*; 56 + let resp = self 57 + .a 58 + .api 59 + .app 60 + .bsky 61 + .actor 62 + .get_profile(ParametersData { actor }.into()) 63 + .await?; 64 + 65 + Ok(resp.data) 66 + } 67 + }
+1
crates/weaver-common/src/compat.rs
··· 1 + // This module contains compatability shims for the atrium_api crate.
+74 -40
crates/weaver-common/src/error.rs
··· 1 + use atrium_api::xrpc::Error as XrpcError; 1 2 use miette::{Diagnostic, NamedSource, SourceOffset, SourceSpan}; 2 3 use std::borrow::Cow; 3 4 use std::fmt; 4 5 5 6 #[derive(thiserror::Error, Debug, Diagnostic)] 6 7 #[error("error(s) in weaver")] 7 - pub struct Error<E: fmt::Debug> { 8 + pub struct Error { 8 9 #[related] 9 - errors: Vec<WeaverErrorKind<E>>, 10 + errors: Vec<WeaverErrorKind>, 10 11 11 12 #[help] 12 13 advice: Option<String>, 13 14 } 14 15 15 16 #[derive(thiserror::Error, Debug, Diagnostic)] 16 - pub enum WeaverErrorKind<E: fmt::Debug> { 17 + pub enum WeaverErrorKind { 17 18 #[error(transparent)] 18 19 #[diagnostic_source] 19 20 ParseError(ParseError), ··· 25 26 TaskError(#[from] n0_future::task::JoinError), 26 27 #[error(transparent)] 27 28 #[diagnostic_source] 28 - AtprotoError(#[from] AtprotoError<E>), 29 + AtprotoError(#[from] AtprotoError), 29 30 #[error(transparent)] 30 31 #[diagnostic_source] 31 32 NetworkError(#[from] NetworkError), ··· 36 37 37 38 #[derive(thiserror::Error, Debug, Diagnostic)] 38 39 #[error("io error")] 39 - pub struct AtprotoError<E: fmt::Debug> { 40 + pub struct AtprotoError { 40 41 #[diagnostic_source] 41 - kind: AtprotoErrorKind<E>, 42 + kind: AtprotoErrorKind, 42 43 } 43 44 44 45 #[derive(thiserror::Error, Debug, Diagnostic)] ··· 52 53 HttpClient(Box<dyn std::error::Error + Send + Sync + 'static>), 53 54 } 54 55 56 + /// Generic error type for XRPC errors. 57 + #[derive(thiserror::Error, Debug, Diagnostic)] 58 + pub enum GenericXrpcError { 59 + Response { 60 + status: http::StatusCode, 61 + error: Option<String>, 62 + }, 63 + Other(String), 64 + } 65 + 66 + impl std::fmt::Display for GenericXrpcError { 67 + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 68 + match self { 69 + Self::Response { status, error } => { 70 + write!(f, "{}", status.as_str())?; 71 + let Some(error) = &error else { 72 + return Ok(()); 73 + }; 74 + if !error.is_empty() { 75 + write!(f, " {error}")?; 76 + } 77 + } 78 + Self::Other(s) => { 79 + write!(f, "{s}")?; 80 + } 81 + } 82 + Ok(()) 83 + } 84 + } 85 + 86 + impl<E> From<XrpcError<E>> for Error 87 + where 88 + E: fmt::Debug, 89 + { 90 + fn from(err: XrpcError<E>) -> Self { 91 + if let XrpcError::XrpcResponse(e) = err { 92 + Self { 93 + errors: vec![WeaverErrorKind::AtprotoError(AtprotoError { 94 + kind: AtprotoErrorKind::AtriumXrpc(GenericXrpcError::Response { 95 + status: e.status, 96 + error: e.error.map(|e| match e { 97 + atrium_xrpc::error::XrpcErrorKind::Custom(_) => { 98 + String::from("custom error") 99 + } 100 + atrium_xrpc::error::XrpcErrorKind::Undefined(res) => res.to_string(), 101 + }), 102 + }), 103 + })], 104 + advice: None, 105 + } 106 + } else { 107 + Self { 108 + errors: vec![WeaverErrorKind::AtprotoError(AtprotoError { 109 + kind: AtprotoErrorKind::AtriumCatchall(GenericXrpcError::Other(format!( 110 + "{:?}", 111 + err 112 + ))), 113 + })], 114 + advice: None, 115 + } 116 + } 117 + } 118 + } 119 + 55 120 #[derive(thiserror::Error, Debug, Diagnostic)] 56 121 #[non_exhaustive] 57 - pub enum AtprotoErrorKind<E: fmt::Debug> { 122 + pub enum AtprotoErrorKind { 58 123 #[error(transparent)] 59 124 #[diagnostic_source] 60 125 AtriumApi(#[from] atrium_api::error::Error), 61 126 #[error("XRPC error: {:?}", .0)] 62 127 #[diagnostic_source] 63 - AtriumXrpc(atrium_api::xrpc::error::XrpcError<E>), 128 + AtriumXrpc(GenericXrpcError), 64 129 #[error("Authentication error: {:?}", .0)] 65 130 #[diagnostic_source] 66 131 Auth(http::HeaderValue), ··· 69 134 UnexpectedResponseType, 70 135 #[error("Atrium error: {:?}", .0)] 71 136 #[diagnostic_source] 72 - AtriumCatchall(atrium_api::xrpc::Error<E>), 137 + AtriumCatchall(GenericXrpcError), 73 138 } 74 139 75 140 #[derive(thiserror::Error, Debug, Diagnostic)] ··· 191 256 atrium_api::xrpc::error::Error::SerdeJson(e) => Ok(Self::from(e)), 192 257 atrium_api::xrpc::error::Error::SerdeHtmlForm(e) => Ok(Self::from(e)), 193 258 _ => Err(err), 194 - } 195 - } 196 - } 197 - 198 - impl<E> From<atrium_api::xrpc::error::Error<E>> for WeaverErrorKind<E> 199 - where 200 - E: fmt::Debug, 201 - { 202 - fn from(err: atrium_api::xrpc::error::Error<E>) -> Self { 203 - use atrium_api::xrpc::error::Error::*; 204 - 205 - match err { 206 - Authentication(e) => { 207 - let atp_err = AtprotoError { 208 - kind: AtprotoErrorKind::Auth(e), 209 - }; 210 - Self::AtprotoError(atp_err) 211 - } 212 - UnexpectedResponseType => { 213 - let atp_err = AtprotoError { 214 - kind: AtprotoErrorKind::UnexpectedResponseType, 215 - }; 216 - Self::AtprotoError(atp_err) 217 - } 218 - XrpcResponse(e) => Self::AtprotoError(AtprotoError { 219 - kind: AtprotoErrorKind::AtriumXrpc(e), 220 - }), 221 - HttpRequest(e) => Self::NetworkError(NetworkError::HttpRequest(e)), 222 - HttpClient(e) => Self::NetworkError(NetworkError::HttpClient(e)), 223 - SerdeJson(e) => Self::SerdeError(SerDeError::SDJson(e)), 224 - SerdeHtmlForm(e) => Self::SerdeError(SerDeError::SerHtmlForm(e)), 225 259 } 226 260 } 227 261 }
+1 -2
crates/weaver-common/src/lib.rs
··· 1 1 pub mod client; 2 + pub mod compat; 2 3 pub mod config; 3 4 pub mod error; 4 5 pub mod lexicons; ··· 8 9 9 10 pub use crate::error::{Error, IoError, ParseError, SerDeError}; 10 11 11 - /// Canonical Cow for us, thanks Amos 12 12 pub use merde::CowStr; 13 - 14 13 /// too many cows, so we have conversions 15 14 pub fn mcow_to_cow(cow: CowStr<'_>) -> std::borrow::Cow<'_, str> { 16 15 match cow {
+140 -22
crates/weaver-common/src/oauth.rs
··· 4 4 did::{CommonDidResolver, CommonDidResolverConfig, DEFAULT_PLC_DIRECTORY_URL}, 5 5 handle::{AtprotoHandleResolver, AtprotoHandleResolverConfig}, 6 6 }; 7 + 7 8 #[cfg(not(feature = "dev"))] 8 - use atrium_oauth::AtprotoClientMetadata; 9 - #[cfg(feature = "dev")] 10 - use atrium_oauth::AtprotoLocalhostClientMetadata; 9 + use atrium_oauth::{AtprotoClientMetadata, GrantType}; 10 + use atrium_oauth::{ 11 + AtprotoLocalhostClientMetadata, AuthorizeOptions, CallbackParams, OAuthSession, 12 + store::{session::SessionStore, state::StateStore}, 13 + }; 11 14 use atrium_oauth::{ 12 15 DefaultHttpClient, KnownScope, OAuthClient, OAuthClientConfig, OAuthResolverConfig, Scope, 13 16 store::{ ··· 18 21 19 22 use std::sync::Arc; 20 23 21 - use crate::resolver::HickoryDnsTxtResolver; 24 + use crate::{client::WeaverHttpClient, resolver::HickoryDnsTxtResolver}; 25 + 26 + pub struct NativeOAuthClient { 27 + oauth: NativeBasicOAuthClient, 28 + pub http_client: Arc<WeaverHttpClient>, 29 + } 30 + 31 + impl NativeOAuthClient { 32 + pub async fn authorize( 33 + &self, 34 + input: impl AsRef<str>, 35 + options: AuthorizeOptions, 36 + ) -> Result<String, atrium_oauth::Error> { 37 + self.oauth.authorize(input, options).await 38 + } 39 + 40 + pub async fn callback( 41 + &self, 42 + params: CallbackParams, 43 + ) -> Result<(NativeBasicOAuthSession, Option<String>), atrium_oauth::Error> { 44 + self.oauth.callback(params).await 45 + } 46 + } 47 + 48 + pub type NativeBasicOAuthSession = OAuthSession< 49 + DefaultHttpClient, 50 + CommonDidResolver<WeaverHttpClient>, 51 + AtprotoHandleResolver<HickoryDnsTxtResolver, WeaverHttpClient>, 52 + MemoryStore<Did, Session>, 53 + >; 54 + 55 + pub type NativeBasicOAuthClient = atrium_oauth::OAuthClient< 56 + MemoryStore<String, InternalStateData>, 57 + MemoryStore<Did, Session>, 58 + CommonDidResolver<WeaverHttpClient>, 59 + AtprotoHandleResolver<HickoryDnsTxtResolver, WeaverHttpClient>, 60 + DefaultHttpClient, 61 + >; 62 + 63 + pub type BasicOAuthClient = atrium_oauth::OAuthClient< 64 + MemoryStore<String, InternalStateData>, 65 + MemoryStore<Did, Session>, 66 + CommonDidResolver<WeaverHttpClient>, 67 + AtprotoHandleResolver<HickoryDnsTxtResolver, WeaverHttpClient>, 68 + DefaultHttpClient, 69 + >; 70 + 71 + pub type WeaverOAuthClient<STATE, SESS> = atrium_oauth::OAuthClient< 72 + STATE, 73 + SESS, 74 + CommonDidResolver<WeaverHttpClient>, 75 + AtprotoHandleResolver<HickoryDnsTxtResolver, WeaverHttpClient>, 76 + DefaultHttpClient, 77 + >; 78 + 79 + pub fn oauth_client<STATE, SESS>( 80 + url: impl AsRef<str>, 81 + jwks: Option<Vec<jose_jwk::Jwk>>, 82 + session_store: SESS, 83 + state_store: STATE, 84 + ) -> Result<WeaverOAuthClient<STATE, SESS>, atrium_oauth::Error> 85 + where 86 + STATE: StateStore + Send + Sync + 'static, 87 + SESS: SessionStore + Send + Sync + 'static, 88 + STATE::Error: std::error::Error + Send + Sync + 'static, 89 + SESS::Error: std::error::Error + Send + Sync + 'static, 90 + { 91 + let http_client = Arc::new(WeaverHttpClient::default()); 92 + let config = OAuthClientConfig { 93 + client_metadata: default_client_metadata(url.as_ref()), 94 + keys: jwks.into(), 95 + resolver: OAuthResolverConfig { 96 + did_resolver: CommonDidResolver::new(CommonDidResolverConfig { 97 + plc_directory_url: DEFAULT_PLC_DIRECTORY_URL.to_string(), 98 + http_client: Arc::clone(&http_client), 99 + }), 100 + handle_resolver: AtprotoHandleResolver::new(AtprotoHandleResolverConfig { 101 + dns_txt_resolver: HickoryDnsTxtResolver::default(), 102 + http_client: Arc::clone(&http_client), 103 + }), 104 + authorization_server_metadata: Default::default(), 105 + protected_resource_metadata: Default::default(), 106 + }, 107 + state_store, 108 + session_store, 109 + }; 110 + let client = OAuthClient::new(config)?; 111 + Ok(client) 112 + } 22 113 23 114 pub fn default_oauth_client( 24 115 url: impl AsRef<str>, 25 - ) -> Result< 26 - atrium_oauth::OAuthClient< 27 - MemoryStore<String, InternalStateData>, 28 - MemoryStore<Did, Session>, 29 - CommonDidResolver<DefaultHttpClient>, 30 - AtprotoHandleResolver<HickoryDnsTxtResolver, DefaultHttpClient>, 31 - DefaultHttpClient, 32 - >, 33 - atrium_oauth::Error, 34 - > { 35 - let http_client = Arc::new(atrium_oauth::DefaultHttpClient::default()); 36 - let keys = if cfg!(feature = "dev") { None } else { todo!() }; 116 + jwks: Option<Vec<jose_jwk::Jwk>>, 117 + ) -> Result<BasicOAuthClient, atrium_oauth::Error> { 118 + let http_client = Arc::new(WeaverHttpClient::default()); 37 119 let config = OAuthClientConfig { 38 120 client_metadata: default_client_metadata(url.as_ref()), 39 - keys, 121 + keys: jwks.into(), 40 122 resolver: OAuthResolverConfig { 41 123 did_resolver: CommonDidResolver::new(CommonDidResolverConfig { 42 124 plc_directory_url: DEFAULT_PLC_DIRECTORY_URL.to_string(), ··· 56 138 Ok(client) 57 139 } 58 140 141 + pub fn default_native_oauth_client() -> Result<NativeOAuthClient, atrium_oauth::Error> { 142 + let http_client = Arc::new(WeaverHttpClient::default()); 143 + let config = OAuthClientConfig { 144 + client_metadata: default_native_client_metadata(), 145 + keys: None, 146 + resolver: OAuthResolverConfig { 147 + did_resolver: CommonDidResolver::new(CommonDidResolverConfig { 148 + plc_directory_url: DEFAULT_PLC_DIRECTORY_URL.to_string(), 149 + http_client: Arc::clone(&http_client), 150 + }), 151 + handle_resolver: AtprotoHandleResolver::new(AtprotoHandleResolverConfig { 152 + dns_txt_resolver: HickoryDnsTxtResolver::default(), 153 + http_client: Arc::clone(&http_client), 154 + }), 155 + authorization_server_metadata: Default::default(), 156 + protected_resource_metadata: Default::default(), 157 + }, 158 + state_store: MemoryStateStore::default(), 159 + session_store: MemorySessionStore::default(), 160 + }; 161 + let client = NativeOAuthClient { 162 + oauth: OAuthClient::new(config)?, 163 + http_client: Arc::clone(&http_client), 164 + }; 165 + Ok(client) 166 + } 167 + 59 168 #[cfg(feature = "dev")] 60 169 pub fn default_client_metadata(_host: &str) -> AtprotoLocalhostClientMetadata { 61 170 AtprotoLocalhostClientMetadata { ··· 67 176 #[cfg(not(feature = "dev"))] 68 177 pub fn default_client_metadata(host: &str) -> AtprotoClientMetadata { 69 178 AtprotoClientMetadata { 70 - client_id: host.to_string(), 71 - redirect_uris: make_redirect_uris(host), 72 - scopes: make_scopes(), 73 - token_endpoint_auth_method: AuthMethod::PrivateKeyJwt, 179 + client_id: format!("{}/oauth/client-metadata.json", host), 180 + client_uri: Some(host.to_string()), 181 + jwks_uri: Some(format!("{}/oauth/jwks.json", host)), 182 + redirect_uris: make_redirect_uris(host).unwrap(), 183 + scopes: make_scopes().unwrap(), 184 + token_endpoint_auth_method: atrium_oauth::AuthMethod::PrivateKeyJwt, 74 185 grant_types: vec![GrantType::AuthorizationCode, GrantType::RefreshToken], 75 186 token_endpoint_auth_signing_alg: Some(String::from("ES256")), 76 187 } 77 188 } 78 189 190 + pub fn default_native_client_metadata() -> AtprotoLocalhostClientMetadata { 191 + AtprotoLocalhostClientMetadata { 192 + redirect_uris: make_redirect_uris("http://127.0.0.1:4000"), 193 + scopes: make_scopes(), 194 + } 195 + } 196 + 79 197 #[inline] 80 198 fn make_redirect_uris(url: &str) -> Option<Vec<String>> { 81 199 Some(vec![format!("{}/oauth/callback", url)]) 82 200 } 83 201 84 202 #[inline] 85 - fn make_scopes() -> Option<Vec<Scope>> { 203 + pub fn make_scopes() -> Option<Vec<Scope>> { 86 204 Some(vec![ 87 205 Scope::Known(KnownScope::Atproto), 88 206 Scope::Known(KnownScope::TransitionGeneric),
+4 -1
flake.nix
··· 272 272 NIX_LD = lib.fileContents "${pkgs.stdenv.cc}/nix-support/dynamic-linker"; 273 273 274 274 LD_LIBRARY_PATH = "$LD_LIBRARY_PATH:$NIX_LD_LIBRARY_PATH"; 275 - DATABASE_URL = "./.db/weaver.db"; 275 + DATABASE_URL = "postgres://postgres:@localhost/weaver_appview"; 276 276 277 277 # Additional dev-shell environment variables can be set directly 278 278 # MY_CUSTOM_DEVELOPMENT_VAR = "something else"; ··· 283 283 nixd 284 284 alejandra 285 285 diesel-cli 286 + postgresql 287 + cargo-watch 288 + jq 286 289 ]; 287 290 }; 288 291 });