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

Compare changes

Choose any two refs to compare.

Changed files
+332 -116
server
upload
+32 -5
README.md
··· 2 3 A (somewhat barebones) atproto app for creating custom sites without hosting! 4 5 - Lexicons are located at [`./lexicons`](./lexicons/) 6 - Server backend is located at [`./server`](./server/) 7 - Upload CLI is located at [`./upload`](./upload/) 8 - Web GUI **will be** located at [`./web-gui`](./web-gui/) 9 - Fuse tool **will be** located at [`./fuse`](./fuse/) 10 11 - TODO: 12 13 - [x] Resolve `handle.host` and `did.did-method.host` domains to records/blobs 14 - [x] Backfill network ··· 16 - [x] Store blobs for CDN (max ~0.25gb per user) 17 - [x] Moderate illegal content or hope that people are niceys 18 (Hope that people are niceys) 19 - - [ ] Finish CLI tool 20 - - [ ] Sign in via app password 21 - - [ ] Clear old site 22 - - [ ] Upload new site 23 - [ ] Fuse uploader 24 - [ ] Web GUI uploader
··· 2 3 A (somewhat barebones) atproto app for creating custom sites without hosting! 4 5 + > [!CAUTION] 6 + > This software is super duper alpha and not tested thoroughly! Use at your own risk! 7 + 8 - Lexicons are located at [`./lexicons`](./lexicons/) 9 - Server backend is located at [`./server`](./server/) 10 - Upload CLI is located at [`./upload`](./upload/) 11 - Web GUI **will be** located at [`./web-gui`](./web-gui/) 12 - Fuse tool **will be** located at [`./fuse`](./fuse/) 13 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: 39 40 - [x] Resolve `handle.host` and `did.did-method.host` domains to records/blobs 41 - [x] Backfill network ··· 43 - [x] Store blobs for CDN (max ~0.25gb per user) 44 - [x] Moderate illegal content or hope that people are niceys 45 (Hope that people are niceys) 46 + - [x] Finish CLI tool 47 + - [x] Sign in via app password 48 + - [x] Clear old site 49 + - [x] Upload new site 50 - [ ] Fuse uploader 51 - [ ] Web GUI uploader
+1
server/deno.json
··· 1 { 2 "tasks": { 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 "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 "dk": "deno run -A --node-modules-dir npm:drizzle-kit" 6 },
··· 1 { 2 "tasks": { 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", 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", 6 "dk": "deno run -A --node-modules-dir npm:drizzle-kit" 7 },
+132 -66
upload/Cargo.lock
··· 158 dependencies = [ 159 "proc-macro2", 160 "quote", 161 - "syn 2.0.106", 162 ] 163 164 [[package]] 165 name = "atcities-upload" 166 - version = "0.1.0" 167 dependencies = [ 168 "clap", 169 "env_logger", ··· 291 "proc-macro2", 292 "quote", 293 "rustversion", 294 - "syn 2.0.106", 295 ] 296 297 [[package]] ··· 526 "heck 0.5.0", 527 "proc-macro2", 528 "quote", 529 - "syn 2.0.106", 530 ] 531 532 [[package]] ··· 697 "proc-macro2", 698 "quote", 699 "strsim", 700 - "syn 2.0.106", 701 ] 702 703 [[package]] ··· 708 dependencies = [ 709 "darling_core", 710 "quote", 711 - "syn 2.0.106", 712 ] 713 714 [[package]] ··· 748 checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" 749 dependencies = [ 750 "data-encoding", 751 - "syn 2.0.106", 752 ] 753 754 [[package]] ··· 801 dependencies = [ 802 "proc-macro2", 803 "quote", 804 - "syn 2.0.106", 805 ] 806 807 [[package]] ··· 856 "heck 0.5.0", 857 "proc-macro2", 858 "quote", 859 - "syn 2.0.106", 860 ] 861 862 [[package]] ··· 1001 dependencies = [ 1002 "proc-macro2", 1003 "quote", 1004 - "syn 2.0.106", 1005 ] 1006 1007 [[package]] ··· 1034 ] 1035 1036 [[package]] 1037 name = "generic-array" 1038 version = "0.14.9" 1039 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1521 1522 [[package]] 1523 name = "indexmap" 1524 - version = "2.11.4" 1525 source = "registry+https://github.com/rust-lang/crates.io-index" 1526 - checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" 1527 dependencies = [ 1528 "equivalent", 1529 "hashbrown 0.16.0", ··· 1594 1595 [[package]] 1596 name = "jacquard" 1597 - version = "0.5.4" 1598 source = "registry+https://github.com/rust-lang/crates.io-index" 1599 - checksum = "c9f7e9e2fb15b8e526c8f44ba197ba8b4174858d05522f088d1ff8de0ce02836" 1600 dependencies = [ 1601 "bon", 1602 "bytes", ··· 1626 1627 [[package]] 1628 name = "jacquard-api" 1629 - version = "0.5.5" 1630 source = "registry+https://github.com/rust-lang/crates.io-index" 1631 - checksum = "eef8307959cc031759816d8e44a3c4db5dd184eedec07431579877a8c4741b80" 1632 dependencies = [ 1633 "bon", 1634 "bytes", ··· 1636 "jacquard-derive", 1637 "miette", 1638 "serde", 1639 "thiserror 2.0.17", 1640 ] 1641 1642 [[package]] 1643 name = "jacquard-common" 1644 - version = "0.5.4" 1645 source = "registry+https://github.com/rust-lang/crates.io-index" 1646 - checksum = "cf91dfa4ade1ca83c9afccee4ece7ed72b6607a0853d88623958d4e7c5382eb3" 1647 dependencies = [ 1648 "base64 0.22.1", 1649 "bon", 1650 "bytes", 1651 "chrono", 1652 "cid", 1653 "getrandom 0.3.4", 1654 "http", 1655 "ipld-core", ··· 1671 "smol_str", 1672 "thiserror 2.0.17", 1673 "tokio", 1674 "trait-variant", 1675 "url", 1676 ] 1677 1678 [[package]] 1679 name = "jacquard-derive" 1680 - version = "0.5.4" 1681 source = "registry+https://github.com/rust-lang/crates.io-index" 1682 - checksum = "4f7db8498da87d842297b169a0080eed751bce173c56626b5fa71261fe72f804" 1683 dependencies = [ 1684 "proc-macro2", 1685 "quote", 1686 - "syn 2.0.106", 1687 ] 1688 1689 [[package]] 1690 name = "jacquard-identity" 1691 - version = "0.5.3" 1692 source = "registry+https://github.com/rust-lang/crates.io-index" 1693 - checksum = "afafcd8fcc26095789aeefe4f54cda4471f1748ba11fa7886184f515362b6339" 1694 dependencies = [ 1695 "bon", 1696 "bytes", ··· 1713 1714 [[package]] 1715 name = "jacquard-oauth" 1716 - version = "0.5.4" 1717 source = "registry+https://github.com/rust-lang/crates.io-index" 1718 - checksum = "0c583fb0f634654b51d390809f4dc5b08087d34c155b33d89b36475f6e6b1428" 1719 dependencies = [ 1720 "base64 0.22.1", 1721 "bytes", ··· 1767 dependencies = [ 1768 "proc-macro2", 1769 "quote", 1770 - "syn 2.0.106", 1771 ] 1772 1773 [[package]] ··· 1872 1873 [[package]] 1874 name = "lexicons" 1875 - version = "0.1.0" 1876 dependencies = [ 1877 "bon", 1878 "bytes", ··· 2007 dependencies = [ 2008 "proc-macro2", 2009 "quote", 2010 - "syn 2.0.106", 2011 ] 2012 2013 [[package]] ··· 2044 2045 [[package]] 2046 name = "mio" 2047 - version = "1.0.4" 2048 source = "registry+https://github.com/rust-lang/crates.io-index" 2049 - checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 2050 dependencies = [ 2051 "libc", 2052 "wasi", 2053 - "windows-sys 0.59.0", 2054 ] 2055 2056 [[package]] ··· 2252 dependencies = [ 2253 "proc-macro2", 2254 "quote", 2255 - "syn 2.0.106", 2256 ] 2257 2258 [[package]] ··· 2294 "proc-macro2", 2295 "proc-macro2-diagnostics", 2296 "quote", 2297 - "syn 2.0.106", 2298 ] 2299 2300 [[package]] ··· 2448 checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 2449 dependencies = [ 2450 "proc-macro2", 2451 - "syn 2.0.106", 2452 ] 2453 2454 [[package]] ··· 2462 2463 [[package]] 2464 name = "proc-macro-error" 2465 version = "1.0.4" 2466 source = "registry+https://github.com/rust-lang/crates.io-index" 2467 checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 2468 dependencies = [ 2469 - "proc-macro-error-attr", 2470 "proc-macro2", 2471 "quote", 2472 "syn 1.0.109", ··· 2475 2476 [[package]] 2477 name = "proc-macro-error-attr" 2478 version = "1.0.4" 2479 source = "registry+https://github.com/rust-lang/crates.io-index" 2480 checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" ··· 2483 "quote", 2484 "version_check", 2485 ] 2486 2487 [[package]] 2488 name = "proc-macro2" ··· 2501 dependencies = [ 2502 "proc-macro2", 2503 "quote", 2504 - "syn 2.0.106", 2505 "version_check", 2506 "yansi", 2507 ] ··· 2989 dependencies = [ 2990 "proc-macro2", 2991 "quote", 2992 - "syn 2.0.106", 2993 ] 2994 2995 [[package]] ··· 3064 version = "1.3.0" 3065 source = "registry+https://github.com/rust-lang/crates.io-index" 3066 checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 3067 - 3068 - [[package]] 3069 - name = "signal-hook-registry" 3070 - version = "1.4.6" 3071 - source = "registry+https://github.com/rust-lang/crates.io-index" 3072 - checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" 3073 - dependencies = [ 3074 - "libc", 3075 - ] 3076 3077 [[package]] 3078 name = "signature" ··· 3165 "ciborium", 3166 "hex_fmt", 3167 "indoc", 3168 - "proc-macro-error", 3169 "proc-macro2", 3170 "quote", 3171 "serde", 3172 "sha2", 3173 - "syn 2.0.106", 3174 "thiserror 1.0.69", 3175 ] 3176 ··· 3226 3227 [[package]] 3228 name = "syn" 3229 - version = "2.0.106" 3230 source = "registry+https://github.com/rust-lang/crates.io-index" 3231 - checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" 3232 dependencies = [ 3233 "proc-macro2", 3234 "quote", ··· 3236 ] 3237 3238 [[package]] 3239 name = "sync_wrapper" 3240 version = "1.0.2" 3241 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3252 dependencies = [ 3253 "proc-macro2", 3254 "quote", 3255 - "syn 2.0.106", 3256 ] 3257 3258 [[package]] ··· 3335 dependencies = [ 3336 "proc-macro2", 3337 "quote", 3338 - "syn 2.0.106", 3339 ] 3340 3341 [[package]] ··· 3346 dependencies = [ 3347 "proc-macro2", 3348 "quote", 3349 - "syn 2.0.106", 3350 ] 3351 3352 [[package]] ··· 3425 "bytes", 3426 "libc", 3427 "mio", 3428 - "parking_lot", 3429 "pin-project-lite", 3430 - "signal-hook-registry", 3431 "socket2 0.6.1", 3432 "tokio-macros", 3433 "windows-sys 0.61.2", ··· 3441 dependencies = [ 3442 "proc-macro2", 3443 "quote", 3444 - "syn 2.0.106", 3445 ] 3446 3447 [[package]] ··· 3541 dependencies = [ 3542 "proc-macro2", 3543 "quote", 3544 - "syn 2.0.106", 3545 ] 3546 3547 [[package]] ··· 3561 dependencies = [ 3562 "proc-macro2", 3563 "quote", 3564 - "syn 2.0.106", 3565 ] 3566 3567 [[package]] ··· 3726 "log", 3727 "proc-macro2", 3728 "quote", 3729 - "syn 2.0.106", 3730 "wasm-bindgen-shared", 3731 ] 3732 ··· 3761 dependencies = [ 3762 "proc-macro2", 3763 "quote", 3764 - "syn 2.0.106", 3765 "wasm-bindgen-backend", 3766 "wasm-bindgen-shared", 3767 ] ··· 3870 dependencies = [ 3871 "proc-macro2", 3872 "quote", 3873 - "syn 2.0.106", 3874 ] 3875 3876 [[package]] ··· 3881 dependencies = [ 3882 "proc-macro2", 3883 "quote", 3884 - "syn 2.0.106", 3885 ] 3886 3887 [[package]] ··· 4288 dependencies = [ 4289 "proc-macro2", 4290 "quote", 4291 - "syn 2.0.106", 4292 "synstructure", 4293 ] 4294 ··· 4309 dependencies = [ 4310 "proc-macro2", 4311 "quote", 4312 - "syn 2.0.106", 4313 ] 4314 4315 [[package]] ··· 4329 dependencies = [ 4330 "proc-macro2", 4331 "quote", 4332 - "syn 2.0.106", 4333 "synstructure", 4334 ] 4335 ··· 4372 dependencies = [ 4373 "proc-macro2", 4374 "quote", 4375 - "syn 2.0.106", 4376 ]
··· 158 dependencies = [ 159 "proc-macro2", 160 "quote", 161 + "syn 2.0.107", 162 ] 163 164 [[package]] 165 name = "atcities-upload" 166 + version = "1.0.1" 167 dependencies = [ 168 "clap", 169 "env_logger", ··· 291 "proc-macro2", 292 "quote", 293 "rustversion", 294 + "syn 2.0.107", 295 ] 296 297 [[package]] ··· 526 "heck 0.5.0", 527 "proc-macro2", 528 "quote", 529 + "syn 2.0.107", 530 ] 531 532 [[package]] ··· 697 "proc-macro2", 698 "quote", 699 "strsim", 700 + "syn 2.0.107", 701 ] 702 703 [[package]] ··· 708 dependencies = [ 709 "darling_core", 710 "quote", 711 + "syn 2.0.107", 712 ] 713 714 [[package]] ··· 748 checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" 749 dependencies = [ 750 "data-encoding", 751 + "syn 2.0.107", 752 ] 753 754 [[package]] ··· 801 dependencies = [ 802 "proc-macro2", 803 "quote", 804 + "syn 2.0.107", 805 ] 806 807 [[package]] ··· 856 "heck 0.5.0", 857 "proc-macro2", 858 "quote", 859 + "syn 2.0.107", 860 ] 861 862 [[package]] ··· 1001 dependencies = [ 1002 "proc-macro2", 1003 "quote", 1004 + "syn 2.0.107", 1005 ] 1006 1007 [[package]] ··· 1034 ] 1035 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]] 1068 name = "generic-array" 1069 version = "0.14.9" 1070 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1552 1553 [[package]] 1554 name = "indexmap" 1555 + version = "2.12.0" 1556 source = "registry+https://github.com/rust-lang/crates.io-index" 1557 + checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" 1558 dependencies = [ 1559 "equivalent", 1560 "hashbrown 0.16.0", ··· 1625 1626 [[package]] 1627 name = "jacquard" 1628 + version = "0.6.0" 1629 source = "registry+https://github.com/rust-lang/crates.io-index" 1630 + checksum = "5857f000e67605995ba7c24c9833f840adf5c063a731a6f72f69e0e3c986791c" 1631 dependencies = [ 1632 "bon", 1633 "bytes", ··· 1657 1658 [[package]] 1659 name = "jacquard-api" 1660 + version = "0.6.0" 1661 source = "registry+https://github.com/rust-lang/crates.io-index" 1662 + checksum = "58132b60473124a34c64c267a1c3390c2e4a4dfd62c801639903c4aefd05f52d" 1663 dependencies = [ 1664 "bon", 1665 "bytes", ··· 1667 "jacquard-derive", 1668 "miette", 1669 "serde", 1670 + "serde_ipld_dagcbor", 1671 "thiserror 2.0.17", 1672 ] 1673 1674 [[package]] 1675 name = "jacquard-common" 1676 + version = "0.6.0" 1677 source = "registry+https://github.com/rust-lang/crates.io-index" 1678 + checksum = "f7f1bab0279ae2e29c3383f132a05b9c708585fab62e94292affdf352ce13242" 1679 dependencies = [ 1680 "base64 0.22.1", 1681 "bon", 1682 "bytes", 1683 "chrono", 1684 "cid", 1685 + "genawaiter", 1686 "getrandom 0.3.4", 1687 "http", 1688 "ipld-core", ··· 1704 "smol_str", 1705 "thiserror 2.0.17", 1706 "tokio", 1707 + "tokio-util", 1708 "trait-variant", 1709 "url", 1710 ] 1711 1712 [[package]] 1713 name = "jacquard-derive" 1714 + version = "0.6.0" 1715 source = "registry+https://github.com/rust-lang/crates.io-index" 1716 + checksum = "a4741844c0638498156338e6230a81ecb54d0a061ffc6f4f54e97b94972a01fb" 1717 dependencies = [ 1718 "proc-macro2", 1719 "quote", 1720 + "syn 2.0.107", 1721 ] 1722 1723 [[package]] 1724 name = "jacquard-identity" 1725 + version = "0.6.0" 1726 source = "registry+https://github.com/rust-lang/crates.io-index" 1727 + checksum = "65ae022725ddc09ce03c1a02a97b895279e1d46957c0574a032fa78b81273263" 1728 dependencies = [ 1729 "bon", 1730 "bytes", ··· 1747 1748 [[package]] 1749 name = "jacquard-oauth" 1750 + version = "0.6.0" 1751 source = "registry+https://github.com/rust-lang/crates.io-index" 1752 + checksum = "8c37269e2ea0c6c3418bf61fb3d342aab87b1bda706ebae49e454f8e50580cab" 1753 dependencies = [ 1754 "base64 0.22.1", 1755 "bytes", ··· 1801 dependencies = [ 1802 "proc-macro2", 1803 "quote", 1804 + "syn 2.0.107", 1805 ] 1806 1807 [[package]] ··· 1906 1907 [[package]] 1908 name = "lexicons" 1909 + version = "1.0.0" 1910 dependencies = [ 1911 "bon", 1912 "bytes", ··· 2041 dependencies = [ 2042 "proc-macro2", 2043 "quote", 2044 + "syn 2.0.107", 2045 ] 2046 2047 [[package]] ··· 2078 2079 [[package]] 2080 name = "mio" 2081 + version = "1.1.0" 2082 source = "registry+https://github.com/rust-lang/crates.io-index" 2083 + checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" 2084 dependencies = [ 2085 "libc", 2086 "wasi", 2087 + "windows-sys 0.61.2", 2088 ] 2089 2090 [[package]] ··· 2286 dependencies = [ 2287 "proc-macro2", 2288 "quote", 2289 + "syn 2.0.107", 2290 ] 2291 2292 [[package]] ··· 2328 "proc-macro2", 2329 "proc-macro2-diagnostics", 2330 "quote", 2331 + "syn 2.0.107", 2332 ] 2333 2334 [[package]] ··· 2482 checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 2483 dependencies = [ 2484 "proc-macro2", 2485 + "syn 2.0.107", 2486 ] 2487 2488 [[package]] ··· 2496 2497 [[package]] 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" 2512 version = "1.0.4" 2513 source = "registry+https://github.com/rust-lang/crates.io-index" 2514 checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 2515 dependencies = [ 2516 + "proc-macro-error-attr 1.0.4", 2517 "proc-macro2", 2518 "quote", 2519 "syn 1.0.109", ··· 2522 2523 [[package]] 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" 2538 version = "1.0.4" 2539 source = "registry+https://github.com/rust-lang/crates.io-index" 2540 checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" ··· 2543 "quote", 2544 "version_check", 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" 2552 2553 [[package]] 2554 name = "proc-macro2" ··· 2567 dependencies = [ 2568 "proc-macro2", 2569 "quote", 2570 + "syn 2.0.107", 2571 "version_check", 2572 "yansi", 2573 ] ··· 3055 dependencies = [ 3056 "proc-macro2", 3057 "quote", 3058 + "syn 2.0.107", 3059 ] 3060 3061 [[package]] ··· 3130 version = "1.3.0" 3131 source = "registry+https://github.com/rust-lang/crates.io-index" 3132 checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 3133 3134 [[package]] 3135 name = "signature" ··· 3222 "ciborium", 3223 "hex_fmt", 3224 "indoc", 3225 + "proc-macro-error 1.0.4", 3226 "proc-macro2", 3227 "quote", 3228 "serde", 3229 "sha2", 3230 + "syn 2.0.107", 3231 "thiserror 1.0.69", 3232 ] 3233 ··· 3283 3284 [[package]] 3285 name = "syn" 3286 + version = "2.0.107" 3287 source = "registry+https://github.com/rust-lang/crates.io-index" 3288 + checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" 3289 dependencies = [ 3290 "proc-macro2", 3291 "quote", ··· 3293 ] 3294 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]] 3307 name = "sync_wrapper" 3308 version = "1.0.2" 3309 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3320 dependencies = [ 3321 "proc-macro2", 3322 "quote", 3323 + "syn 2.0.107", 3324 ] 3325 3326 [[package]] ··· 3403 dependencies = [ 3404 "proc-macro2", 3405 "quote", 3406 + "syn 2.0.107", 3407 ] 3408 3409 [[package]] ··· 3414 dependencies = [ 3415 "proc-macro2", 3416 "quote", 3417 + "syn 2.0.107", 3418 ] 3419 3420 [[package]] ··· 3493 "bytes", 3494 "libc", 3495 "mio", 3496 "pin-project-lite", 3497 "socket2 0.6.1", 3498 "tokio-macros", 3499 "windows-sys 0.61.2", ··· 3507 dependencies = [ 3508 "proc-macro2", 3509 "quote", 3510 + "syn 2.0.107", 3511 ] 3512 3513 [[package]] ··· 3607 dependencies = [ 3608 "proc-macro2", 3609 "quote", 3610 + "syn 2.0.107", 3611 ] 3612 3613 [[package]] ··· 3627 dependencies = [ 3628 "proc-macro2", 3629 "quote", 3630 + "syn 2.0.107", 3631 ] 3632 3633 [[package]] ··· 3792 "log", 3793 "proc-macro2", 3794 "quote", 3795 + "syn 2.0.107", 3796 "wasm-bindgen-shared", 3797 ] 3798 ··· 3827 dependencies = [ 3828 "proc-macro2", 3829 "quote", 3830 + "syn 2.0.107", 3831 "wasm-bindgen-backend", 3832 "wasm-bindgen-shared", 3833 ] ··· 3936 dependencies = [ 3937 "proc-macro2", 3938 "quote", 3939 + "syn 2.0.107", 3940 ] 3941 3942 [[package]] ··· 3947 dependencies = [ 3948 "proc-macro2", 3949 "quote", 3950 + "syn 2.0.107", 3951 ] 3952 3953 [[package]] ··· 4354 dependencies = [ 4355 "proc-macro2", 4356 "quote", 4357 + "syn 2.0.107", 4358 "synstructure", 4359 ] 4360 ··· 4375 dependencies = [ 4376 "proc-macro2", 4377 "quote", 4378 + "syn 2.0.107", 4379 ] 4380 4381 [[package]] ··· 4395 dependencies = [ 4396 "proc-macro2", 4397 "quote", 4398 + "syn 2.0.107", 4399 "synstructure", 4400 ] 4401 ··· 4438 dependencies = [ 4439 "proc-macro2", 4440 "quote", 4441 + "syn 2.0.107", 4442 ]
+3 -3
upload/Cargo.toml
··· 4 5 [package] 6 name = "atcities-upload" 7 - version = "0.1.0" 8 edition = "2024" 9 10 [dependencies] ··· 13 ignore = "0.4.23" 14 regex = "1.12.2" 15 mime_guess = "2.0.5" 16 - tokio = { version = "1.48.0", features = ["full"] } 17 - jacquard = "0.5.4" 18 reqwest = "0.12.24" 19 env_logger = "0.11.8" 20 miette = { version = "7.6.0", features = ["derive", "fancy"] }
··· 4 5 [package] 6 name = "atcities-upload" 7 + version = "1.0.1" 8 edition = "2024" 9 10 [dependencies] ··· 13 ignore = "0.4.23" 14 regex = "1.12.2" 15 mime_guess = "2.0.5" 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"] }
+3 -3
upload/lexicons/Cargo.toml
··· 1 [package] 2 name = "lexicons" 3 - version = "0.1.0" 4 edition = "2024" 5 6 [features] ··· 13 miette = "7.6.0" 14 serde = "1.0.228" 15 thiserror = "2.0.17" 16 - jacquard-common = "0.5.3" 17 - jacquard-derive = "0.5.3"
··· 1 [package] 2 name = "lexicons" 3 + version = "1.0.0" 4 edition = "2024" 5 6 [features] ··· 13 miette = "7.6.0" 14 serde = "1.0.228" 15 thiserror = "2.0.17" 16 + jacquard-common = "0.6.0" 17 + jacquard-derive = "0.6.0"
+149 -39
upload/src/main.rs
··· 1 use clap::{ArgAction, Parser}; 2 - use jacquard::api::com_atproto::repo::apply_writes::{self, ApplyWrites, ApplyWritesWritesItem}; 3 - use jacquard::atproto; 4 use jacquard::client::MemorySessionStore; 5 - use jacquard::prelude::XrpcClient; 6 use jacquard::types::string::{AtStrError, RecordKey, Rkey}; 7 use jacquard::{ 8 Data, 9 api::com_atproto::{self, repo::list_records::ListRecords}, ··· 13 types::{ident::AtIdentifier, nsid::Nsid, string::AtprotoStr, uri::Uri}, 14 xrpc::XrpcExt, 15 }; 16 - use miette::{ErrReport, IntoDiagnostic, Result}; 17 use std::{collections::HashMap, fs, path::PathBuf}; 18 19 use crate::sitemap::{BlobRef, Sitemap, SitemapNode}; ··· 21 mod sitemap; 22 mod utils; 23 24 - #[derive(Parser, Debug)] 25 #[command(version, about, long_about = None)] 26 struct Config { 27 /// Handle or DID to authenticate 28 - #[arg(verbatim_doc_comment, short, long)] 29 user: String, 30 /// App password to authenticate the client 31 /// Normal passwords also work but are not advised 32 - #[arg(verbatim_doc_comment, short, long)] 33 - password: String, 34 35 /// Include dotfiles in upload 36 /// Default: false ··· 48 dir: PathBuf, 49 } 50 51 - #[tokio::main] 52 - async fn main() -> Result<(), miette::Error> { 53 - env_logger::init(); 54 - // get config items 55 - let config = Config::parse(); 56 - 57 - // get local site info 58 - let local_sitemap = sitemap::local_sitemap(config.dir, config.all_files, config.git_ignore)?; 59 - 60 - // create session 61 - let client = JacquardResolver::default(); 62 - let store = MemorySessionStore::default(); 63 - let session = CredentialSession::new(store.into(), client.into()); 64 - 65 - let auth = session 66 - .login(config.user.into(), config.password.into(), None, None, None) 67 - .await?; 68 - println!("Authenticated as {}", auth.did); 69 - 70 - 71 - let agent = Agent::from(session); 72 - 73 // find live site records 74 let mut cursor = None; 75 let mut remote_records = Vec::new(); 76 loop { 77 let req = com_atproto::repo::list_records::ListRecords::new() 78 .collection( 79 Nsid::new("dev.atcities.route").expect("failed to generate dev.atcities.route nsid"), 80 ) 81 - .repo(AtIdentifier::Did(auth.did.clone())) 82 .limit(100) 83 .maybe_cursor(cursor) 84 .build(); ··· 120 break; 121 } 122 } 123 124 // upload local site blobs 125 let mut new_sitemap: Sitemap = HashMap::new(); 126 for (k, v) in local_sitemap { 127 let blob = match v.blob { 128 BlobRef::Local(path) => path, 129 BlobRef::Remote(_) => { ··· 131 } 132 }; 133 let blob = fs::read(blob).into_diagnostic()?; 134 - // let res = agent 135 - // .upload_blob(blob, v.mime_type.clone().into()) 136 - // .await?; 137 138 let req = com_atproto::repo::upload_blob::UploadBlob::new() 139 .body(blob.into()) ··· 144 k, 145 SitemapNode { 146 mime_type: v.mime_type, 147 - blob: BlobRef::Remote(res.blob), 148 }, 149 ); 150 } 151 152 // batch delete/upload records 153 let mut writes = Vec::new(); 154 let mut delete_records = remote_records ··· 168 let mut create_records = new_sitemap 169 .into_iter() 170 .map(|(k, v)| { 171 - let rkey = RecordKey(Rkey::new_owned(k).into_diagnostic()?); 172 let blob = match v.blob { 173 BlobRef::Local(_) => panic!("Illegal local blob"), 174 BlobRef::Remote(cid) => cid, ··· 178 "$type": "dev.atcities.route#blob", 179 "blob": { 180 "$type": "blob", 181 - "ref": blob.r#ref.as_str(), 182 "mimeType": blob.mime_type.0.as_str(), 183 "size": blob.size 184 } ··· 197 writes.append(&mut delete_records); 198 writes.append(&mut create_records); 199 200 let req = com_atproto::repo::apply_writes::ApplyWrites::new() 201 - .repo(AtIdentifier::Did(auth.did.clone())) 202 .writes(writes) 203 .build(); 204 205 let res = agent 206 .xrpc(agent.endpoint().await) 207 .send::<ApplyWrites>(&req) 208 .await? 209 .into_output()?; 210 211 - println!("res: {res:#?}"); 212 213 Ok(()) 214 }
··· 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; 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}; 11 use jacquard::{ 12 Data, 13 api::com_atproto::{self, repo::list_records::ListRecords}, ··· 17 types::{ident::AtIdentifier, nsid::Nsid, string::AtprotoStr, uri::Uri}, 18 xrpc::XrpcExt, 19 }; 20 + use miette::{Context, IntoDiagnostic, Result}; 21 + use std::io::Write; 22 use std::{collections::HashMap, fs, path::PathBuf}; 23 24 use crate::sitemap::{BlobRef, Sitemap, SitemapNode}; ··· 26 mod sitemap; 27 mod utils; 28 29 + #[derive(Parser, Debug, Clone)] 30 #[command(version, about, long_about = None)] 31 struct Config { 32 /// Handle or DID to authenticate 33 + #[arg(verbatim_doc_comment)] 34 user: String, 35 + 36 /// App password to authenticate the client 37 /// Normal passwords also work but are not advised 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>, 42 43 /// Include dotfiles in upload 44 /// Default: false ··· 56 dir: PathBuf, 57 } 58 59 + async fn live_records(agent: &impl AgentSessionExt, config: Config) -> Result<Vec<String>> { 60 // find live site records 61 let mut cursor = None; 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 + }; 69 loop { 70 let req = com_atproto::repo::list_records::ListRecords::new() 71 .collection( 72 Nsid::new("dev.atcities.route").expect("failed to generate dev.atcities.route nsid"), 73 ) 74 + .repo(user.clone()) 75 .limit(100) 76 .maybe_cursor(cursor) 77 .build(); ··· 113 break; 114 } 115 } 116 + Ok(remote_records) 117 + } 118 119 + async fn upload_site_blobs( 120 + agent: &impl AgentSessionExt, 121 + _config: Config, 122 + local_sitemap: Sitemap, 123 + ) -> Result<Sitemap> { 124 // upload local site blobs 125 let mut new_sitemap: Sitemap = HashMap::new(); 126 for (k, v) in local_sitemap { 127 + print!("Uploading {k}... "); 128 + let _ = std::io::stdout().flush(); 129 let blob = match v.blob { 130 BlobRef::Local(path) => path, 131 BlobRef::Remote(_) => { ··· 133 } 134 }; 135 let blob = fs::read(blob).into_diagnostic()?; 136 137 let req = com_atproto::repo::upload_blob::UploadBlob::new() 138 .body(blob.into()) ··· 143 k, 144 SitemapNode { 145 mime_type: v.mime_type, 146 + blob: BlobRef::Remote(res.blob.into()), 147 }, 148 ); 149 + 150 + println!("Done!"); 151 } 152 153 + Ok(new_sitemap) 154 + } 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>> { 163 // batch delete/upload records 164 let mut writes = Vec::new(); 165 let mut delete_records = remote_records ··· 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, ··· 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 } ··· 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 + }; 322 323 Ok(()) 324 }
+12
upload/src/utils.rs
··· 1 use regex::Regex; 2 3 pub fn rkey_to_url(rkey: String) -> Option<String> { 4 let regex = Regex::new( 5 // symbols A-Za-z0-9 -._~: are all valid rkey characters ··· 62 63 Some(res) 64 }
··· 1 use regex::Regex; 2 3 + #[allow(dead_code)] 4 pub fn rkey_to_url(rkey: String) -> Option<String> { 5 let regex = Regex::new( 6 // symbols A-Za-z0-9 -._~: are all valid rkey characters ··· 63 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 + } 76 + }