[WIP] A (somewhat barebones) atproto app for creating custom sites without hosting!

Compare changes

Choose any two refs to compare.

Changed files
+644 -194
server
upload
+32 -5
README.md
··· 2 2 3 3 A (somewhat barebones) atproto app for creating custom sites without hosting! 4 4 5 + > [!CAUTION] 6 + > This software is super duper alpha and not tested thoroughly! Use at your own risk! 7 + 5 8 - Lexicons are located at [`./lexicons`](./lexicons/) 6 9 - Server backend is located at [`./server`](./server/) 7 10 - Upload CLI is located at [`./upload`](./upload/) 8 11 - Web GUI **will be** located at [`./web-gui`](./web-gui/) 9 12 - Fuse tool **will be** located at [`./fuse`](./fuse/) 10 13 11 - TODO: 14 + ## How to: 15 + 16 + **View someones site?** 17 + > Head to `https://<handle>.atcities.dev/` or `https://<did>.did-<method>.atcities.dev/`. 18 + 19 + **Upload your own site?** 20 + > Use the upload cli. Installation guide below. 21 + 22 + **Self host atcities.dev?** 23 + > Clone this repo and setup the `.env` file for `/server`, then run `deno run run` 24 + 25 + ### CLI installation guide. 26 + 27 + 1. Build from source: 28 + Prerequisites: `git` `cargo` `rust` 29 + ```sh 30 + git clone https://tangled.org/@vielle.dev/atcities.dev 31 + cd atcities.dev/upload 32 + cargo build 33 + sudo cp ./target/debug/atcities-upload /bin 34 + # Reload your shell environment 35 + atcities-upload --version 36 + ``` 37 + 38 + ## Todo: 12 39 13 40 - [x] Resolve `handle.host` and `did.did-method.host` domains to records/blobs 14 41 - [x] Backfill network ··· 16 43 - [x] Store blobs for CDN (max ~0.25gb per user) 17 44 - [x] Moderate illegal content or hope that people are niceys 18 45 (Hope that people are niceys) 19 - - [ ] Finish CLI tool 20 - - [ ] Sign in via app password 21 - - [ ] Clear old site 22 - - [ ] Upload new site 46 + - [x] Finish CLI tool 47 + - [x] Sign in via app password 48 + - [x] Clear old site 49 + - [x] Upload new site 23 50 - [ ] Fuse uploader 24 51 - [ ] Web GUI uploader
+1
server/deno.json
··· 1 1 { 2 2 "tasks": { 3 3 "dev": "deno run --watch --allow-net --allow-env --allow-sys --allow-read=/usr/bin/ldd,./blobs,./src --allow-write=./blobs --allow-ffi --env-file src/index.ts", 4 + "run": "deno run --allow-net --allow-env --allow-sys --allow-read=/usr/bin/ldd,./blobs,./src --allow-write=./blobs --allow-ffi --env-file src/index.ts", 4 5 "lexgen": "deno run --allow-env --allow-sys --allow-read=.. --allow-write=./src/lexicons --no-prompt @atcute/lex-cli generate -c ./lex.config.js && cat ./src/lexicons/index.ts | sed \"s/.js/.ts/\" > ./src/lexicons/index.ts", 5 6 "dk": "deno run -A --node-modules-dir npm:drizzle-kit" 6 7 },
+355 -67
upload/Cargo.lock
··· 22 22 ] 23 23 24 24 [[package]] 25 + name = "addr2line" 26 + version = "0.25.1" 27 + source = "registry+https://github.com/rust-lang/crates.io-index" 28 + checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" 29 + dependencies = [ 30 + "gimli", 31 + ] 32 + 33 + [[package]] 25 34 name = "adler2" 26 35 version = "2.0.1" 27 36 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 149 158 dependencies = [ 150 159 "proc-macro2", 151 160 "quote", 152 - "syn 2.0.106", 161 + "syn 2.0.107", 153 162 ] 154 163 155 164 [[package]] 156 165 name = "atcities-upload" 157 - version = "0.1.0" 166 + version = "1.0.1" 158 167 dependencies = [ 159 168 "clap", 169 + "env_logger", 160 170 "ignore", 161 171 "jacquard", 172 + "lexicons", 173 + "miette", 162 174 "mime_guess", 163 175 "regex", 164 176 "reqwest", 177 + "thiserror 2.0.17", 165 178 "tokio", 166 179 ] 167 180 ··· 178 191 checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 179 192 180 193 [[package]] 194 + name = "backtrace" 195 + version = "0.3.76" 196 + source = "registry+https://github.com/rust-lang/crates.io-index" 197 + checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" 198 + dependencies = [ 199 + "addr2line", 200 + "cfg-if", 201 + "libc", 202 + "miniz_oxide", 203 + "object", 204 + "rustc-demangle", 205 + "windows-link 0.2.1", 206 + ] 207 + 208 + [[package]] 209 + name = "backtrace-ext" 210 + version = "0.2.1" 211 + source = "registry+https://github.com/rust-lang/crates.io-index" 212 + checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" 213 + dependencies = [ 214 + "backtrace", 215 + ] 216 + 217 + [[package]] 181 218 name = "base-x" 182 219 version = "0.2.11" 183 220 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 254 291 "proc-macro2", 255 292 "quote", 256 293 "rustversion", 257 - "syn 2.0.106", 294 + "syn 2.0.107", 258 295 ] 259 296 260 297 [[package]] ··· 489 526 "heck 0.5.0", 490 527 "proc-macro2", 491 528 "quote", 492 - "syn 2.0.106", 529 + "syn 2.0.107", 493 530 ] 494 531 495 532 [[package]] ··· 660 697 "proc-macro2", 661 698 "quote", 662 699 "strsim", 663 - "syn 2.0.106", 700 + "syn 2.0.107", 664 701 ] 665 702 666 703 [[package]] ··· 671 708 dependencies = [ 672 709 "darling_core", 673 710 "quote", 674 - "syn 2.0.106", 711 + "syn 2.0.107", 675 712 ] 676 713 677 714 [[package]] ··· 711 748 checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" 712 749 dependencies = [ 713 750 "data-encoding", 714 - "syn 2.0.106", 751 + "syn 2.0.107", 715 752 ] 716 753 717 754 [[package]] ··· 764 801 dependencies = [ 765 802 "proc-macro2", 766 803 "quote", 767 - "syn 2.0.106", 804 + "syn 2.0.107", 768 805 ] 769 806 770 807 [[package]] ··· 819 856 "heck 0.5.0", 820 857 "proc-macro2", 821 858 "quote", 822 - "syn 2.0.106", 859 + "syn 2.0.107", 860 + ] 861 + 862 + [[package]] 863 + name = "env_filter" 864 + version = "0.1.4" 865 + source = "registry+https://github.com/rust-lang/crates.io-index" 866 + checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" 867 + dependencies = [ 868 + "log", 869 + "regex", 870 + ] 871 + 872 + [[package]] 873 + name = "env_logger" 874 + version = "0.11.8" 875 + source = "registry+https://github.com/rust-lang/crates.io-index" 876 + checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" 877 + dependencies = [ 878 + "anstream", 879 + "anstyle", 880 + "env_filter", 881 + "jiff", 882 + "log", 823 883 ] 824 884 825 885 [[package]] ··· 934 994 checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 935 995 936 996 [[package]] 997 + name = "futures-macro" 998 + version = "0.3.31" 999 + source = "registry+https://github.com/rust-lang/crates.io-index" 1000 + checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 1001 + dependencies = [ 1002 + "proc-macro2", 1003 + "quote", 1004 + "syn 2.0.107", 1005 + ] 1006 + 1007 + [[package]] 937 1008 name = "futures-sink" 938 1009 version = "0.3.31" 939 1010 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 952 1023 checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 953 1024 dependencies = [ 954 1025 "futures-core", 1026 + "futures-io", 1027 + "futures-macro", 1028 + "futures-sink", 955 1029 "futures-task", 1030 + "memchr", 956 1031 "pin-project-lite", 957 1032 "pin-utils", 958 1033 "slab", 959 1034 ] 960 1035 961 1036 [[package]] 1037 + name = "genawaiter" 1038 + version = "0.99.1" 1039 + source = "registry+https://github.com/rust-lang/crates.io-index" 1040 + checksum = "c86bd0361bcbde39b13475e6e36cb24c329964aa2611be285289d1e4b751c1a0" 1041 + dependencies = [ 1042 + "futures-core", 1043 + "genawaiter-macro", 1044 + "genawaiter-proc-macro", 1045 + "proc-macro-hack", 1046 + ] 1047 + 1048 + [[package]] 1049 + name = "genawaiter-macro" 1050 + version = "0.99.1" 1051 + source = "registry+https://github.com/rust-lang/crates.io-index" 1052 + checksum = "0b32dfe1fdfc0bbde1f22a5da25355514b5e450c33a6af6770884c8750aedfbc" 1053 + 1054 + [[package]] 1055 + name = "genawaiter-proc-macro" 1056 + version = "0.99.1" 1057 + source = "registry+https://github.com/rust-lang/crates.io-index" 1058 + checksum = "784f84eebc366e15251c4a8c3acee82a6a6f427949776ecb88377362a9621738" 1059 + dependencies = [ 1060 + "proc-macro-error 0.4.12", 1061 + "proc-macro-hack", 1062 + "proc-macro2", 1063 + "quote", 1064 + "syn 1.0.109", 1065 + ] 1066 + 1067 + [[package]] 962 1068 name = "generic-array" 963 1069 version = "0.14.9" 964 1070 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 995 1101 "wasip2", 996 1102 "wasm-bindgen", 997 1103 ] 1104 + 1105 + [[package]] 1106 + name = "gimli" 1107 + version = "0.32.3" 1108 + source = "registry+https://github.com/rust-lang/crates.io-index" 1109 + checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" 998 1110 999 1111 [[package]] 1000 1112 name = "globset" ··· 1440 1552 1441 1553 [[package]] 1442 1554 name = "indexmap" 1443 - version = "2.11.4" 1555 + version = "2.12.0" 1444 1556 source = "registry+https://github.com/rust-lang/crates.io-index" 1445 - checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" 1557 + checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" 1446 1558 dependencies = [ 1447 1559 "equivalent", 1448 1560 "hashbrown 0.16.0", ··· 1494 1606 ] 1495 1607 1496 1608 [[package]] 1609 + name = "is_ci" 1610 + version = "1.2.0" 1611 + source = "registry+https://github.com/rust-lang/crates.io-index" 1612 + checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" 1613 + 1614 + [[package]] 1497 1615 name = "is_terminal_polyfill" 1498 1616 version = "1.70.1" 1499 1617 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1507 1625 1508 1626 [[package]] 1509 1627 name = "jacquard" 1510 - version = "0.5.3" 1628 + version = "0.6.0" 1511 1629 source = "registry+https://github.com/rust-lang/crates.io-index" 1512 - checksum = "bf257386e29370eb7387093736d59f2aeda3ac692a37481c7201bc5f9cc82fae" 1630 + checksum = "5857f000e67605995ba7c24c9833f840adf5c063a731a6f72f69e0e3c986791c" 1513 1631 dependencies = [ 1514 1632 "bon", 1515 1633 "bytes", ··· 1539 1657 1540 1658 [[package]] 1541 1659 name = "jacquard-api" 1542 - version = "0.5.3" 1660 + version = "0.6.0" 1543 1661 source = "registry+https://github.com/rust-lang/crates.io-index" 1544 - checksum = "e67619aa60467ee687633e5face5743835e5f9d8e57c387a8d830cfd2ba8d2c8" 1662 + checksum = "58132b60473124a34c64c267a1c3390c2e4a4dfd62c801639903c4aefd05f52d" 1545 1663 dependencies = [ 1546 1664 "bon", 1547 1665 "bytes", ··· 1549 1667 "jacquard-derive", 1550 1668 "miette", 1551 1669 "serde", 1670 + "serde_ipld_dagcbor", 1552 1671 "thiserror 2.0.17", 1553 1672 ] 1554 1673 1555 1674 [[package]] 1556 1675 name = "jacquard-common" 1557 - version = "0.5.3" 1676 + version = "0.6.0" 1558 1677 source = "registry+https://github.com/rust-lang/crates.io-index" 1559 - checksum = "8b1c05d479eee46004ba3ec495e7802b7c02cc429396aeb7296a558b9cb6e6ce" 1678 + checksum = "f7f1bab0279ae2e29c3383f132a05b9c708585fab62e94292affdf352ce13242" 1560 1679 dependencies = [ 1561 1680 "base64 0.22.1", 1562 1681 "bon", 1563 1682 "bytes", 1564 1683 "chrono", 1565 1684 "cid", 1685 + "genawaiter", 1566 1686 "getrandom 0.3.4", 1567 1687 "http", 1568 1688 "ipld-core", ··· 1584 1704 "smol_str", 1585 1705 "thiserror 2.0.17", 1586 1706 "tokio", 1707 + "tokio-util", 1587 1708 "trait-variant", 1588 1709 "url", 1589 1710 ] 1590 1711 1591 1712 [[package]] 1592 1713 name = "jacquard-derive" 1593 - version = "0.5.3" 1714 + version = "0.6.0" 1594 1715 source = "registry+https://github.com/rust-lang/crates.io-index" 1595 - checksum = "aadace50223141e60d16e26a28c8c8a9b647f291814986010234cf1c130367ab" 1716 + checksum = "a4741844c0638498156338e6230a81ecb54d0a061ffc6f4f54e97b94972a01fb" 1596 1717 dependencies = [ 1597 1718 "proc-macro2", 1598 1719 "quote", 1599 - "syn 2.0.106", 1720 + "syn 2.0.107", 1600 1721 ] 1601 1722 1602 1723 [[package]] 1603 1724 name = "jacquard-identity" 1604 - version = "0.5.3" 1725 + version = "0.6.0" 1605 1726 source = "registry+https://github.com/rust-lang/crates.io-index" 1606 - checksum = "afafcd8fcc26095789aeefe4f54cda4471f1748ba11fa7886184f515362b6339" 1727 + checksum = "65ae022725ddc09ce03c1a02a97b895279e1d46957c0574a032fa78b81273263" 1607 1728 dependencies = [ 1608 1729 "bon", 1609 1730 "bytes", ··· 1626 1747 1627 1748 [[package]] 1628 1749 name = "jacquard-oauth" 1629 - version = "0.5.3" 1750 + version = "0.6.0" 1630 1751 source = "registry+https://github.com/rust-lang/crates.io-index" 1631 - checksum = "597e74c6697a449e834936dde22c30165f0f2da0c38b42ec599173b58d73a098" 1752 + checksum = "8c37269e2ea0c6c3418bf61fb3d342aab87b1bda706ebae49e454f8e50580cab" 1632 1753 dependencies = [ 1633 1754 "base64 0.22.1", 1634 1755 "bytes", ··· 1660 1781 ] 1661 1782 1662 1783 [[package]] 1784 + name = "jiff" 1785 + version = "0.2.15" 1786 + source = "registry+https://github.com/rust-lang/crates.io-index" 1787 + checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" 1788 + dependencies = [ 1789 + "jiff-static", 1790 + "log", 1791 + "portable-atomic", 1792 + "portable-atomic-util", 1793 + "serde", 1794 + ] 1795 + 1796 + [[package]] 1797 + name = "jiff-static" 1798 + version = "0.2.15" 1799 + source = "registry+https://github.com/rust-lang/crates.io-index" 1800 + checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" 1801 + dependencies = [ 1802 + "proc-macro2", 1803 + "quote", 1804 + "syn 2.0.107", 1805 + ] 1806 + 1807 + [[package]] 1663 1808 name = "jni" 1664 1809 version = "0.21.1" 1665 1810 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1761 1906 1762 1907 [[package]] 1763 1908 name = "lexicons" 1764 - version = "0.1.0" 1909 + version = "1.0.0" 1765 1910 dependencies = [ 1766 1911 "bon", 1767 1912 "bytes", ··· 1875 2020 source = "registry+https://github.com/rust-lang/crates.io-index" 1876 2021 checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" 1877 2022 dependencies = [ 2023 + "backtrace", 2024 + "backtrace-ext", 1878 2025 "cfg-if", 1879 2026 "miette-derive", 1880 - "unicode-width", 2027 + "owo-colors", 2028 + "supports-color", 2029 + "supports-hyperlinks", 2030 + "supports-unicode", 2031 + "terminal_size", 2032 + "textwrap", 2033 + "unicode-width 0.1.14", 1881 2034 ] 1882 2035 1883 2036 [[package]] ··· 1888 2041 dependencies = [ 1889 2042 "proc-macro2", 1890 2043 "quote", 1891 - "syn 2.0.106", 2044 + "syn 2.0.107", 1892 2045 ] 1893 2046 1894 2047 [[package]] ··· 1925 2078 1926 2079 [[package]] 1927 2080 name = "mio" 1928 - version = "1.0.4" 2081 + version = "1.1.0" 1929 2082 source = "registry+https://github.com/rust-lang/crates.io-index" 1930 - checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 2083 + checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" 1931 2084 dependencies = [ 1932 2085 "libc", 1933 2086 "wasi", 1934 - "windows-sys 0.59.0", 2087 + "windows-sys 0.61.2", 1935 2088 ] 1936 2089 1937 2090 [[package]] ··· 2090 2243 ] 2091 2244 2092 2245 [[package]] 2246 + name = "object" 2247 + version = "0.37.3" 2248 + source = "registry+https://github.com/rust-lang/crates.io-index" 2249 + checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" 2250 + dependencies = [ 2251 + "memchr", 2252 + ] 2253 + 2254 + [[package]] 2093 2255 name = "once_cell" 2094 2256 version = "1.21.3" 2095 2257 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2124 2286 dependencies = [ 2125 2287 "proc-macro2", 2126 2288 "quote", 2127 - "syn 2.0.106", 2289 + "syn 2.0.107", 2128 2290 ] 2129 2291 2130 2292 [[package]] ··· 2166 2328 "proc-macro2", 2167 2329 "proc-macro2-diagnostics", 2168 2330 "quote", 2169 - "syn 2.0.106", 2331 + "syn 2.0.107", 2170 2332 ] 2333 + 2334 + [[package]] 2335 + name = "owo-colors" 2336 + version = "4.2.3" 2337 + source = "registry+https://github.com/rust-lang/crates.io-index" 2338 + checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" 2171 2339 2172 2340 [[package]] 2173 2341 name = "p256" ··· 2269 2437 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 2270 2438 2271 2439 [[package]] 2440 + name = "portable-atomic" 2441 + version = "1.11.1" 2442 + source = "registry+https://github.com/rust-lang/crates.io-index" 2443 + checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" 2444 + 2445 + [[package]] 2446 + name = "portable-atomic-util" 2447 + version = "0.2.4" 2448 + source = "registry+https://github.com/rust-lang/crates.io-index" 2449 + checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" 2450 + dependencies = [ 2451 + "portable-atomic", 2452 + ] 2453 + 2454 + [[package]] 2272 2455 name = "potential_utf" 2273 2456 version = "0.1.3" 2274 2457 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2299 2482 checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 2300 2483 dependencies = [ 2301 2484 "proc-macro2", 2302 - "syn 2.0.106", 2485 + "syn 2.0.107", 2303 2486 ] 2304 2487 2305 2488 [[package]] ··· 2313 2496 2314 2497 [[package]] 2315 2498 name = "proc-macro-error" 2499 + version = "0.4.12" 2500 + source = "registry+https://github.com/rust-lang/crates.io-index" 2501 + checksum = "18f33027081eba0a6d8aba6d1b1c3a3be58cbb12106341c2d5759fcd9b5277e7" 2502 + dependencies = [ 2503 + "proc-macro-error-attr 0.4.12", 2504 + "proc-macro2", 2505 + "quote", 2506 + "syn 1.0.109", 2507 + "version_check", 2508 + ] 2509 + 2510 + [[package]] 2511 + name = "proc-macro-error" 2316 2512 version = "1.0.4" 2317 2513 source = "registry+https://github.com/rust-lang/crates.io-index" 2318 2514 checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 2319 2515 dependencies = [ 2320 - "proc-macro-error-attr", 2516 + "proc-macro-error-attr 1.0.4", 2321 2517 "proc-macro2", 2322 2518 "quote", 2323 2519 "syn 1.0.109", ··· 2326 2522 2327 2523 [[package]] 2328 2524 name = "proc-macro-error-attr" 2525 + version = "0.4.12" 2526 + source = "registry+https://github.com/rust-lang/crates.io-index" 2527 + checksum = "8a5b4b77fdb63c1eca72173d68d24501c54ab1269409f6b672c85deb18af69de" 2528 + dependencies = [ 2529 + "proc-macro2", 2530 + "quote", 2531 + "syn 1.0.109", 2532 + "syn-mid", 2533 + "version_check", 2534 + ] 2535 + 2536 + [[package]] 2537 + name = "proc-macro-error-attr" 2329 2538 version = "1.0.4" 2330 2539 source = "registry+https://github.com/rust-lang/crates.io-index" 2331 2540 checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" ··· 2334 2543 "quote", 2335 2544 "version_check", 2336 2545 ] 2546 + 2547 + [[package]] 2548 + name = "proc-macro-hack" 2549 + version = "0.5.20+deprecated" 2550 + source = "registry+https://github.com/rust-lang/crates.io-index" 2551 + checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" 2337 2552 2338 2553 [[package]] 2339 2554 name = "proc-macro2" ··· 2352 2567 dependencies = [ 2353 2568 "proc-macro2", 2354 2569 "quote", 2355 - "syn 2.0.106", 2570 + "syn 2.0.107", 2356 2571 "version_check", 2357 2572 "yansi", 2358 2573 ] ··· 2585 2800 "url", 2586 2801 "wasm-bindgen", 2587 2802 "wasm-bindgen-futures", 2803 + "wasm-streams", 2588 2804 "web-sys", 2589 2805 "webpki-roots", 2590 2806 ] ··· 2664 2880 ] 2665 2881 2666 2882 [[package]] 2883 + name = "rustc-demangle" 2884 + version = "0.1.26" 2885 + source = "registry+https://github.com/rust-lang/crates.io-index" 2886 + checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 2887 + 2888 + [[package]] 2667 2889 name = "rustc-hash" 2668 2890 version = "2.1.1" 2669 2891 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2684 2906 2685 2907 [[package]] 2686 2908 name = "rustls" 2687 - version = "0.23.32" 2909 + version = "0.23.33" 2688 2910 source = "registry+https://github.com/rust-lang/crates.io-index" 2689 - checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" 2911 + checksum = "751e04a496ca00bb97a5e043158d23d66b5aabf2e1d5aa2a0aaebb1aafe6f82c" 2690 2912 dependencies = [ 2691 2913 "once_cell", 2692 2914 "ring", ··· 2833 3055 dependencies = [ 2834 3056 "proc-macro2", 2835 3057 "quote", 2836 - "syn 2.0.106", 3058 + "syn 2.0.107", 2837 3059 ] 2838 3060 2839 3061 [[package]] ··· 2910 3132 checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 2911 3133 2912 3134 [[package]] 2913 - name = "signal-hook-registry" 2914 - version = "1.4.6" 2915 - source = "registry+https://github.com/rust-lang/crates.io-index" 2916 - checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" 2917 - dependencies = [ 2918 - "libc", 2919 - ] 2920 - 2921 - [[package]] 2922 3135 name = "signature" 2923 3136 version = "2.2.0" 2924 3137 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3009 3222 "ciborium", 3010 3223 "hex_fmt", 3011 3224 "indoc", 3012 - "proc-macro-error", 3225 + "proc-macro-error 1.0.4", 3013 3226 "proc-macro2", 3014 3227 "quote", 3015 3228 "serde", 3016 3229 "sha2", 3017 - "syn 2.0.106", 3230 + "syn 2.0.107", 3018 3231 "thiserror 1.0.69", 3019 3232 ] 3020 3233 ··· 3037 3250 checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 3038 3251 3039 3252 [[package]] 3253 + name = "supports-color" 3254 + version = "3.0.2" 3255 + source = "registry+https://github.com/rust-lang/crates.io-index" 3256 + checksum = "c64fc7232dd8d2e4ac5ce4ef302b1d81e0b80d055b9d77c7c4f51f6aa4c867d6" 3257 + dependencies = [ 3258 + "is_ci", 3259 + ] 3260 + 3261 + [[package]] 3262 + name = "supports-hyperlinks" 3263 + version = "3.1.0" 3264 + source = "registry+https://github.com/rust-lang/crates.io-index" 3265 + checksum = "804f44ed3c63152de6a9f90acbea1a110441de43006ea51bcce8f436196a288b" 3266 + 3267 + [[package]] 3268 + name = "supports-unicode" 3269 + version = "3.0.0" 3270 + source = "registry+https://github.com/rust-lang/crates.io-index" 3271 + checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" 3272 + 3273 + [[package]] 3040 3274 name = "syn" 3041 3275 version = "1.0.109" 3042 3276 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3049 3283 3050 3284 [[package]] 3051 3285 name = "syn" 3052 - version = "2.0.106" 3286 + version = "2.0.107" 3053 3287 source = "registry+https://github.com/rust-lang/crates.io-index" 3054 - checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 3288 + checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" 3055 3289 dependencies = [ 3056 3290 "proc-macro2", 3057 3291 "quote", ··· 3059 3293 ] 3060 3294 3061 3295 [[package]] 3296 + name = "syn-mid" 3297 + version = "0.5.4" 3298 + source = "registry+https://github.com/rust-lang/crates.io-index" 3299 + checksum = "fea305d57546cc8cd04feb14b62ec84bf17f50e3f7b12560d7bfa9265f39d9ed" 3300 + dependencies = [ 3301 + "proc-macro2", 3302 + "quote", 3303 + "syn 1.0.109", 3304 + ] 3305 + 3306 + [[package]] 3062 3307 name = "sync_wrapper" 3063 3308 version = "1.0.2" 3064 3309 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3075 3320 dependencies = [ 3076 3321 "proc-macro2", 3077 3322 "quote", 3078 - "syn 2.0.106", 3323 + "syn 2.0.107", 3079 3324 ] 3080 3325 3081 3326 [[package]] ··· 3113 3358 ] 3114 3359 3115 3360 [[package]] 3361 + name = "terminal_size" 3362 + version = "0.4.3" 3363 + source = "registry+https://github.com/rust-lang/crates.io-index" 3364 + checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" 3365 + dependencies = [ 3366 + "rustix", 3367 + "windows-sys 0.60.2", 3368 + ] 3369 + 3370 + [[package]] 3371 + name = "textwrap" 3372 + version = "0.16.2" 3373 + source = "registry+https://github.com/rust-lang/crates.io-index" 3374 + checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" 3375 + dependencies = [ 3376 + "unicode-linebreak", 3377 + "unicode-width 0.2.2", 3378 + ] 3379 + 3380 + [[package]] 3116 3381 name = "thiserror" 3117 3382 version = "1.0.69" 3118 3383 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3138 3403 dependencies = [ 3139 3404 "proc-macro2", 3140 3405 "quote", 3141 - "syn 2.0.106", 3406 + "syn 2.0.107", 3142 3407 ] 3143 3408 3144 3409 [[package]] ··· 3149 3414 dependencies = [ 3150 3415 "proc-macro2", 3151 3416 "quote", 3152 - "syn 2.0.106", 3417 + "syn 2.0.107", 3153 3418 ] 3154 3419 3155 3420 [[package]] ··· 3228 3493 "bytes", 3229 3494 "libc", 3230 3495 "mio", 3231 - "parking_lot", 3232 3496 "pin-project-lite", 3233 - "signal-hook-registry", 3234 3497 "socket2 0.6.1", 3235 3498 "tokio-macros", 3236 3499 "windows-sys 0.61.2", ··· 3244 3507 dependencies = [ 3245 3508 "proc-macro2", 3246 3509 "quote", 3247 - "syn 2.0.106", 3510 + "syn 2.0.107", 3248 3511 ] 3249 3512 3250 3513 [[package]] ··· 3344 3607 dependencies = [ 3345 3608 "proc-macro2", 3346 3609 "quote", 3347 - "syn 2.0.106", 3610 + "syn 2.0.107", 3348 3611 ] 3349 3612 3350 3613 [[package]] ··· 3364 3627 dependencies = [ 3365 3628 "proc-macro2", 3366 3629 "quote", 3367 - "syn 2.0.106", 3630 + "syn 2.0.107", 3368 3631 ] 3369 3632 3370 3633 [[package]] ··· 3401 3664 checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" 3402 3665 3403 3666 [[package]] 3667 + name = "unicode-linebreak" 3668 + version = "0.1.5" 3669 + source = "registry+https://github.com/rust-lang/crates.io-index" 3670 + checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" 3671 + 3672 + [[package]] 3404 3673 name = "unicode-width" 3405 3674 version = "0.1.14" 3406 3675 source = "registry+https://github.com/rust-lang/crates.io-index" 3407 3676 checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 3677 + 3678 + [[package]] 3679 + name = "unicode-width" 3680 + version = "0.2.2" 3681 + source = "registry+https://github.com/rust-lang/crates.io-index" 3682 + checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" 3408 3683 3409 3684 [[package]] 3410 3685 name = "unsigned-varint" ··· 3517 3792 "log", 3518 3793 "proc-macro2", 3519 3794 "quote", 3520 - "syn 2.0.106", 3795 + "syn 2.0.107", 3521 3796 "wasm-bindgen-shared", 3522 3797 ] 3523 3798 ··· 3552 3827 dependencies = [ 3553 3828 "proc-macro2", 3554 3829 "quote", 3555 - "syn 2.0.106", 3830 + "syn 2.0.107", 3556 3831 "wasm-bindgen-backend", 3557 3832 "wasm-bindgen-shared", 3558 3833 ] ··· 3564 3839 checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" 3565 3840 dependencies = [ 3566 3841 "unicode-ident", 3842 + ] 3843 + 3844 + [[package]] 3845 + name = "wasm-streams" 3846 + version = "0.4.2" 3847 + source = "registry+https://github.com/rust-lang/crates.io-index" 3848 + checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" 3849 + dependencies = [ 3850 + "futures-util", 3851 + "js-sys", 3852 + "wasm-bindgen", 3853 + "wasm-bindgen-futures", 3854 + "web-sys", 3567 3855 ] 3568 3856 3569 3857 [[package]] ··· 3648 3936 dependencies = [ 3649 3937 "proc-macro2", 3650 3938 "quote", 3651 - "syn 2.0.106", 3939 + "syn 2.0.107", 3652 3940 ] 3653 3941 3654 3942 [[package]] ··· 3659 3947 dependencies = [ 3660 3948 "proc-macro2", 3661 3949 "quote", 3662 - "syn 2.0.106", 3950 + "syn 2.0.107", 3663 3951 ] 3664 3952 3665 3953 [[package]] ··· 4066 4354 dependencies = [ 4067 4355 "proc-macro2", 4068 4356 "quote", 4069 - "syn 2.0.106", 4357 + "syn 2.0.107", 4070 4358 "synstructure", 4071 4359 ] 4072 4360 ··· 4087 4375 dependencies = [ 4088 4376 "proc-macro2", 4089 4377 "quote", 4090 - "syn 2.0.106", 4378 + "syn 2.0.107", 4091 4379 ] 4092 4380 4093 4381 [[package]] ··· 4107 4395 dependencies = [ 4108 4396 "proc-macro2", 4109 4397 "quote", 4110 - "syn 2.0.106", 4398 + "syn 2.0.107", 4111 4399 "synstructure", 4112 4400 ] 4113 4401 ··· 4150 4438 dependencies = [ 4151 4439 "proc-macro2", 4152 4440 "quote", 4153 - "syn 2.0.106", 4441 + "syn 2.0.107", 4154 4442 ]
+8 -12
upload/Cargo.toml
··· 4 4 5 5 [package] 6 6 name = "atcities-upload" 7 - version = "0.1.0" 7 + version = "1.0.1" 8 8 edition = "2024" 9 9 10 10 [dependencies] 11 - # lexicons = { path = "./lexicons" } 11 + lexicons = { path = "./lexicons" } 12 12 clap = { version = "4.5.49", features = ["derive"] } 13 13 ignore = "0.4.23" 14 14 regex = "1.12.2" 15 15 mime_guess = "2.0.5" 16 - tokio = { version = "1.48.0", features = ["full"] } 17 - jacquard = { version = "0.5.3", features = ["api", "dns"] } 18 - reqwest = { version = "0.12.24", features = [ 19 - "__tls", 20 - "charset", 21 - "default-tls", 22 - "h2", 23 - "http2", 24 - "system-proxy", 25 - ] } 16 + tokio = { version = "1.48.0", features = [] } 17 + jacquard = "0.6.0" 18 + reqwest = "0.12.24" 19 + env_logger = "0.11.8" 20 + miette = { version = "7.6.0", features = ["derive", "fancy"] } 21 + thiserror = "2.0.17"
+3 -3
upload/lexicons/Cargo.toml
··· 1 1 [package] 2 2 name = "lexicons" 3 - version = "0.1.0" 3 + version = "1.0.0" 4 4 edition = "2024" 5 5 6 6 [features] ··· 13 13 miette = "7.6.0" 14 14 serde = "1.0.228" 15 15 thiserror = "2.0.17" 16 - jacquard-common = "0.5.3" 17 - jacquard-derive = "0.5.3" 16 + jacquard-common = "0.6.0" 17 + jacquard-derive = "0.6.0"
+215 -64
upload/src/main.rs
··· 1 1 use clap::{ArgAction, Parser}; 2 + use jacquard::api::com_atproto::repo::apply_writes::{ 3 + self, ApplyWrites, ApplyWritesOutput, ApplyWritesWritesItem, 4 + }; 5 + use jacquard::client::AgentSessionExt; 2 6 use jacquard::client::MemorySessionStore; 7 + use jacquard::oauth::loopback::LoopbackConfig; 8 + use jacquard::oauth::types::AuthorizeOptions; 9 + use jacquard::types::string::{AtStrError, RecordKey, Rkey}; 10 + use jacquard::{AuthorizationToken, CowStr, atproto, oauth}; 3 11 use jacquard::{ 4 12 Data, 5 13 api::com_atproto::{self, repo::list_records::ListRecords}, 6 - client::{Agent, AgentSessionExt, credential_session::CredentialSession}, 14 + client::{Agent, credential_session::CredentialSession}, 7 15 cowstr::ToCowStr, 8 16 identity::JacquardResolver, 9 17 types::{ident::AtIdentifier, nsid::Nsid, string::AtprotoStr, uri::Uri}, 10 18 xrpc::XrpcExt, 11 19 }; 20 + use miette::{Context, IntoDiagnostic, Result}; 21 + use std::io::Write; 12 22 use std::{collections::HashMap, fs, path::PathBuf}; 13 23 14 24 use crate::sitemap::{BlobRef, Sitemap, SitemapNode}; ··· 16 26 mod sitemap; 17 27 mod utils; 18 28 19 - #[derive(Parser, Debug)] 29 + #[derive(Parser, Debug, Clone)] 20 30 #[command(version, about, long_about = None)] 21 31 struct Config { 22 32 /// Handle or DID to authenticate 23 - #[arg(verbatim_doc_comment, short, long)] 33 + #[arg(verbatim_doc_comment)] 24 34 user: String, 35 + 25 36 /// App password to authenticate the client 26 37 /// Normal passwords also work but are not advised 27 - #[arg(verbatim_doc_comment, short, long)] 28 - password: String, 38 + /// If ommited, oauth will be used instead 39 + /// Oauth is reccomended where possible. 40 + #[arg(verbatim_doc_comment, short = 'p', long = "password")] 41 + password: Option<String>, 29 42 30 43 /// Include dotfiles in upload 31 44 /// Default: false ··· 43 56 dir: PathBuf, 44 57 } 45 58 46 - #[tokio::main] 47 - async fn main() -> Result<(), ()> { 48 - // get config items 49 - let config = Config::parse(); 50 - 51 - // get local site info 52 - let local_sitemap = sitemap::local_sitemap(config.dir, config.all_files, config.git_ignore) 53 - .or_else(|err| { 54 - println!("Sitemap Error: {}", err); 55 - Err(()) 56 - })?; 57 - 58 - // create session 59 - let client = JacquardResolver::default(); 60 - let store = MemorySessionStore::default(); 61 - let session = CredentialSession::new(store.into(), client.into()); 62 - 63 - let auth = match session 64 - .login(config.user.into(), config.password.into(), None, None, None) 65 - .await 66 - { 67 - Ok(val) => { 68 - println!("Authenticated {} ({})", val.handle, val.did); 69 - val 70 - } 71 - Err(err) => { 72 - println!("Got error while authenticating:\n{}", err); 73 - return Err(()); 74 - } 75 - }; 76 - 77 - let agent = Agent::from(session); 78 - 59 + async fn live_records(agent: &impl AgentSessionExt, config: Config) -> Result<Vec<String>> { 79 60 // find live site records 80 61 let mut cursor = None; 81 62 let mut remote_records = Vec::new(); 63 + let user = config.user.clone(); 64 + let user = if user.contains(":") { 65 + AtIdentifier::Did(user.into()) 66 + } else { 67 + AtIdentifier::Handle(user.into()) 68 + }; 82 69 loop { 83 70 let req = com_atproto::repo::list_records::ListRecords::new() 84 71 .collection( 85 72 Nsid::new("dev.atcities.route").expect("failed to generate dev.atcities.route nsid"), 86 73 ) 87 - .repo(AtIdentifier::Did(auth.did.clone())) 74 + .repo(user.clone()) 88 75 .limit(100) 89 76 .maybe_cursor(cursor) 90 77 .build(); ··· 92 79 let res = agent 93 80 .xrpc(agent.endpoint().await) 94 81 .send::<ListRecords>(&req) 95 - .await 96 - .or_else(|err| { 97 - println!("Error fetching records: {err}"); 98 - Err(()) 99 - })? 100 - .into_output() 101 - .or_else(|err| { 102 - println!("Error fetching records: {err}"); 103 - Err(()) 104 - })?; 82 + .await? 83 + .into_output()?; 105 84 106 85 for record in res.records { 107 86 match record { ··· 134 113 break; 135 114 } 136 115 } 116 + Ok(remote_records) 117 + } 137 118 138 - println!("{remote_records:?}"); 139 - 119 + async fn upload_site_blobs( 120 + agent: &impl AgentSessionExt, 121 + _config: Config, 122 + local_sitemap: Sitemap, 123 + ) -> Result<Sitemap> { 140 124 // upload local site blobs 141 125 let mut new_sitemap: Sitemap = HashMap::new(); 142 126 for (k, v) in local_sitemap { 127 + print!("Uploading {k}... "); 128 + let _ = std::io::stdout().flush(); 143 129 let blob = match v.blob { 144 130 BlobRef::Local(path) => path, 145 131 BlobRef::Remote(_) => { 146 132 panic!("Impossible state") 147 133 } 148 134 }; 149 - let blob = fs::read(blob).or_else(|err| { 150 - println!("FS error: {err}"); 151 - Err(()) 152 - })?; 153 - let res = agent 154 - .upload_blob(blob, v.mime_type.clone().into()) 155 - .await 156 - .or_else(|err| { 157 - println!("Blob upload error: {err}"); 158 - Err(()) 159 - })?; 135 + let blob = fs::read(blob).into_diagnostic()?; 136 + 137 + let req = com_atproto::repo::upload_blob::UploadBlob::new() 138 + .body(blob.into()) 139 + .build(); 140 + let res = agent.send(req).await?.into_output()?; 141 + 160 142 new_sitemap.insert( 161 143 k, 162 144 SitemapNode { 163 145 mime_type: v.mime_type, 164 - blob: BlobRef::Remote(res.r#ref), 146 + blob: BlobRef::Remote(res.blob.into()), 165 147 }, 166 148 ); 149 + 150 + println!("Done!"); 167 151 } 168 - println!("{:#?}", new_sitemap); 152 + 153 + Ok(new_sitemap) 154 + } 169 155 156 + async fn update_remote_site( 157 + agent: &impl AgentSessionExt, 158 + config: Config, 159 + auth: AuthorizationToken<'_>, 160 + remote_records: Vec<String>, 161 + new_sitemap: Sitemap, 162 + ) -> Result<ApplyWritesOutput<'static>> { 170 163 // batch delete/upload records 164 + let mut writes = Vec::new(); 165 + let mut delete_records = remote_records 166 + .into_iter() 167 + .map(|x| { 168 + let rkey = RecordKey(Rkey::new_owned(x)?); 169 + Ok(ApplyWritesWritesItem::Delete(Box::new( 170 + apply_writes::Delete::builder() 171 + .collection(Nsid::raw("dev.atcities.route")) 172 + .rkey(rkey) 173 + .build(), 174 + ))) 175 + }) 176 + .collect::<Result<Vec<ApplyWritesWritesItem<'_>>, AtStrError>>() 177 + .into_diagnostic()?; 178 + 179 + let mut create_records = new_sitemap 180 + .into_iter() 181 + .map(|(k, v)| { 182 + let k = match k.as_str() { 183 + "404.html" => String::from("404"), 184 + "index.html" => String::from("/"), 185 + _ => match k.strip_suffix("/index.html") { 186 + Some(k) => format!("/{k}/"), 187 + None => format!("/{k}"), 188 + } 189 + }; 190 + let rkey = 191 + utils::url_to_rkey(k).wrap_err("Invalid file path. Could not be converted to rkey")?; 192 + let rkey = RecordKey(Rkey::new_owned(rkey).into_diagnostic()?); 193 + let blob = match v.blob { 194 + BlobRef::Local(_) => panic!("Illegal local blob"), 195 + BlobRef::Remote(cid) => cid, 196 + }; 197 + let data = atproto!({ 198 + "page": { 199 + "$type": "dev.atcities.route#blob", 200 + "blob": { 201 + "$type": "blob", 202 + "ref": { 203 + "$link": blob.r#ref.as_str() 204 + }, 205 + "mimeType": blob.mime_type.0.as_str(), 206 + "size": blob.size 207 + } 208 + } 209 + }); 210 + Ok(ApplyWritesWritesItem::Create(Box::new( 211 + apply_writes::Create::builder() 212 + .collection(Nsid::raw("dev.atcities.route")) 213 + .rkey(rkey) 214 + .value(data) 215 + .build(), 216 + ))) 217 + }) 218 + .collect::<Result<Vec<ApplyWritesWritesItem<'_>>, miette::Error>>()?; 219 + 220 + writes.append(&mut delete_records); 221 + writes.append(&mut create_records); 222 + 223 + let repo = if config.user.contains(":") { 224 + AtIdentifier::Did(config.user.into()) 225 + } else { 226 + AtIdentifier::Handle(config.user.into()) 227 + }; 228 + 229 + let req = com_atproto::repo::apply_writes::ApplyWrites::new() 230 + .repo(repo) 231 + .writes(writes) 232 + .build(); 233 + 234 + let res = agent 235 + .xrpc(agent.endpoint().await) 236 + .auth(auth) 237 + .send::<ApplyWrites>(&req) 238 + .await? 239 + .into_output()?; 240 + 241 + Ok(res) 242 + } 243 + 244 + #[tokio::main] 245 + async fn main() -> Result<(), miette::Error> { 246 + env_logger::init(); 247 + // get config items 248 + let config = Config::parse(); 249 + 250 + // get local site info 251 + let local_sitemap = 252 + sitemap::local_sitemap(config.dir.clone(), config.all_files, config.git_ignore)?; 253 + 254 + // create session 255 + if let Some(password) = config.password.clone() { 256 + let password = password.into(); 257 + let client = JacquardResolver::default(); 258 + let store = MemorySessionStore::default(); 259 + let session = CredentialSession::new(store.into(), client.into()); 260 + 261 + let auth = session 262 + .login(config.user.clone().into(), password, None, None, None) 263 + .await?; 264 + 265 + let agent = Agent::from(session); 266 + 267 + let remote_sitemap = live_records(&agent, config.clone()).await?; 268 + let new_sitemap = upload_site_blobs(&agent, config.clone(), local_sitemap).await?; 269 + let _ = update_remote_site( 270 + &agent, 271 + config.clone(), 272 + AuthorizationToken::Bearer(auth.access_jwt), 273 + remote_sitemap, 274 + new_sitemap, 275 + ) 276 + .await?; 277 + 278 + println!( 279 + "Site is now updated. Live at {}", 280 + utils::site_handle(config.user) 281 + ); 282 + } else { 283 + let oauth = oauth::client::OAuthClient::with_memory_store(); 284 + let session = oauth 285 + .login_with_local_server( 286 + config.user.clone(), 287 + AuthorizeOptions::default(), 288 + LoopbackConfig::default(), 289 + ) 290 + .await?; 291 + 292 + // sick and twisted reference mangling BUT it works So 293 + // tldr: the cowstr is a borrowed cowstr iiuc, 294 + // so it needs to be turned into an owned cowstr 295 + // to break reference to session which gets moved 296 + let auth = session.access_token().await; 297 + let auth = match auth { 298 + AuthorizationToken::Bearer(cow_str) => CowStr::copy_from_str(cow_str.as_str()), 299 + AuthorizationToken::Dpop(cow_str) => CowStr::copy_from_str(cow_str.as_str()), 300 + }; 301 + 302 + println!("{}", auth); 303 + 304 + let agent = Agent::from(session); 305 + 306 + let remote_sitemap = live_records(&agent, config.clone()).await?; 307 + let new_sitemap = upload_site_blobs(&agent, config.clone(), local_sitemap).await?; 308 + let _ = update_remote_site( 309 + &agent, 310 + config.clone(), 311 + AuthorizationToken::Dpop(auth), 312 + remote_sitemap, 313 + new_sitemap, 314 + ) 315 + .await?; 316 + 317 + println!( 318 + "Site is now updated. Live at {}", 319 + utils::site_handle(config.user) 320 + ); 321 + }; 171 322 172 323 Ok(()) 173 324 }
+16 -41
upload/src/sitemap.rs
··· 1 1 use ignore::WalkBuilder; 2 - use jacquard::types::cid::Cid; 2 + use jacquard::types::blob::Blob; 3 + use miette::Diagnostic; 3 4 use mime_guess::mime; 4 - use std::{collections::HashMap, error::Error, fmt, path::PathBuf}; 5 + use std::{collections::HashMap, path::PathBuf}; 6 + use thiserror::Error; 5 7 6 8 pub type Sitemap = HashMap<String, SitemapNode>; 7 9 ··· 14 16 #[derive(Debug)] 15 17 pub enum BlobRef { 16 18 Local(PathBuf), 17 - Remote(Cid<'static>), 19 + Remote(Blob<'static>), 18 20 } 19 21 20 - #[derive(Debug)] 22 + #[derive(Debug, Error, Diagnostic)] 21 23 pub enum SitemapErr { 22 - IoErr(std::io::Error), 23 - IgnoreErr(ignore::Error), 24 - StripPrefixErr(std::path::StripPrefixError), 24 + #[error("IO error: {}", .0)] 25 + IoErr(#[from] std::io::Error), 26 + #[error("Error finding files: {}", .0)] 27 + IgnoreErr(#[from] ignore::Error), 28 + #[error("Error normalising file paths: {}", .0)] 29 + StripPrefixErr(#[from] std::path::StripPrefixError), 30 + #[error("Found non UTF-8 path")] 25 31 NotUTF8Path, 26 32 } 27 33 28 - impl From<std::io::Error> for SitemapErr { 29 - fn from(value: std::io::Error) -> Self { 30 - SitemapErr::IoErr(value) 31 - } 32 - } 33 - 34 - impl From<ignore::Error> for SitemapErr { 35 - fn from(value: ignore::Error) -> Self { 36 - SitemapErr::IgnoreErr(value) 37 - } 38 - } 39 - 40 - impl From<std::path::StripPrefixError> for SitemapErr { 41 - fn from(value: std::path::StripPrefixError) -> Self { 42 - SitemapErr::StripPrefixErr(value) 43 - } 44 - } 45 - 46 - impl Error for SitemapErr {} 47 - 48 - impl fmt::Display for SitemapErr { 49 - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 50 - match self { 51 - SitemapErr::IoErr(err) => write!(f, "IO Error: {}", err), 52 - SitemapErr::IgnoreErr(err) => write!(f, "Ignore Error: {}", err), 53 - SitemapErr::StripPrefixErr(err) => write!(f, "File Path Error: {}", err), 54 - SitemapErr::NotUTF8Path => write!(f, "Found a path that was not UTF-8"), 55 - } 56 - } 57 - } 58 - 59 - pub fn local_sitemap<'a>( 34 + pub fn local_sitemap( 60 35 dir: PathBuf, 61 36 include_dotfiles: bool, 62 37 use_gitignore: bool, 63 - ) -> Result<Sitemap, SitemapErr> { 38 + ) -> miette::Result<Sitemap, SitemapErr> { 64 39 let prefix = dir.canonicalize()?; 65 40 66 41 let mut res = HashMap::new(); ··· 87 62 // etc 88 63 let key = file.path().canonicalize()?; 89 64 let key = key.strip_prefix(&prefix)?; 90 - let key = String::from(key.to_str().ok_or_else(|| SitemapErr::NotUTF8Path)?); 65 + let key = String::from(key.to_str().ok_or(SitemapErr::NotUTF8Path)?); 91 66 92 67 let mime = mime_guess::from_path(&key); 93 68 let mime = mime.first().unwrap_or(mime::APPLICATION_OCTET_STREAM);
+14 -2
upload/src/utils.rs
··· 1 1 use regex::Regex; 2 2 3 + #[allow(dead_code)] 3 4 pub fn rkey_to_url(rkey: String) -> Option<String> { 4 5 let regex = Regex::new( 5 6 // symbols A-Za-z0-9 -._~: are all valid rkey characters ··· 32 33 .replace(":3D", "=") 33 34 .replace(":40", "@"); 34 35 35 - return Some(res); 36 + Some(res) 36 37 } 37 38 38 39 pub fn url_to_rkey(url: String) -> Option<String> { ··· 60 61 .replace("=", ":3D") 61 62 .replace("@", ":40"); 62 63 63 - return Some(res); 64 + Some(res) 65 + } 66 + 67 + pub fn site_handle(user: String) -> String { 68 + if user.contains(":") { 69 + let user = user.split(":").collect::<Vec<_>>(); 70 + let method = user[1]; 71 + let did = user[2]; 72 + format!("https://{did}.did-{method}.atcities.dev/") 73 + } else { 74 + format!("https://{user}.atcities.dev/") 75 + } 64 76 }