A local-first private AI assistant for everyday use. Runs on-device models with encrypted P2P sync, and supports sharing chats publicly on ATProto.
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

Added Atproto authentication commands (#129)

* wip: tried atrium for atproto login

* wip: generated auth url for atproto w bluesky

- nxt callback and saving the tokens, then write records

* wip: Done with callback processing and session retrival

- nxtup: Persist the session by impl SessionStore

* feat: At proto login/logout complete with commands

authored by

Anandu Pavanan and committed by
GitHub
f1cfbe1f d7bf3261

+932 -47
+388 -12
Cargo.lock
··· 41 41 dependencies = [ 42 42 "cfg-if", 43 43 "cipher", 44 - "cpufeatures", 44 + "cpufeatures 0.2.17", 45 45 ] 46 46 47 47 [[package]] ··· 491 491 ] 492 492 493 493 [[package]] 494 + name = "async-compression" 495 + version = "0.4.41" 496 + source = "registry+https://github.com/rust-lang/crates.io-index" 497 + checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" 498 + dependencies = [ 499 + "compression-codecs", 500 + "compression-core", 501 + "pin-project-lite", 502 + "tokio", 503 + ] 504 + 505 + [[package]] 494 506 name = "async-executor" 495 507 version = "1.14.0" 496 508 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 659 671 checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 660 672 661 673 [[package]] 674 + name = "atrium-api" 675 + version = "0.25.8" 676 + source = "registry+https://github.com/rust-lang/crates.io-index" 677 + checksum = "e04c38c6bfc922a7950a13ad52f8d21bee77020b90e822364c7ec8625c4cd403" 678 + dependencies = [ 679 + "atrium-common", 680 + "atrium-xrpc", 681 + "chrono", 682 + "http", 683 + "ipld-core", 684 + "langtag", 685 + "regex", 686 + "serde", 687 + "serde_bytes", 688 + "serde_json", 689 + "thiserror 1.0.69", 690 + "tokio", 691 + "trait-variant", 692 + ] 693 + 694 + [[package]] 695 + name = "atrium-common" 696 + version = "0.1.4" 697 + source = "registry+https://github.com/rust-lang/crates.io-index" 698 + checksum = "98466259d2e6189ddf3439b96432d5303668b4d51c2a78996934d981065550f3" 699 + dependencies = [ 700 + "dashmap", 701 + "lru", 702 + "moka", 703 + "thiserror 1.0.69", 704 + "tokio", 705 + "trait-variant", 706 + "web-time", 707 + ] 708 + 709 + [[package]] 710 + name = "atrium-identity" 711 + version = "0.1.9" 712 + source = "registry+https://github.com/rust-lang/crates.io-index" 713 + checksum = "c11c7225bc15376200cbb81fb9c91a9ce8000edac76484dc636cca2ec200f673" 714 + dependencies = [ 715 + "atrium-api", 716 + "atrium-common", 717 + "atrium-xrpc", 718 + "serde", 719 + "serde_html_form", 720 + "serde_json", 721 + "thiserror 1.0.69", 722 + "trait-variant", 723 + ] 724 + 725 + [[package]] 726 + name = "atrium-oauth" 727 + version = "0.1.7" 728 + source = "registry+https://github.com/rust-lang/crates.io-index" 729 + checksum = "8d027eb47a804181f5a0632cf0af816227a89a64aa0c1ac99e92e999dcc4939b" 730 + dependencies = [ 731 + "atrium-api", 732 + "atrium-common", 733 + "atrium-identity", 734 + "atrium-xrpc", 735 + "base64", 736 + "chrono", 737 + "dashmap", 738 + "ecdsa", 739 + "elliptic-curve", 740 + "jose-jwa", 741 + "jose-jwk", 742 + "p256", 743 + "rand 0.8.5", 744 + "reqwest", 745 + "serde", 746 + "serde_html_form", 747 + "serde_json", 748 + "sha2 0.10.9", 749 + "thiserror 1.0.69", 750 + "tokio", 751 + "trait-variant", 752 + ] 753 + 754 + [[package]] 755 + name = "atrium-xrpc" 756 + version = "0.12.4" 757 + source = "registry+https://github.com/rust-lang/crates.io-index" 758 + checksum = "944b35cc08732d40ddbb3356be9e38d11aed4b4c40c33f5b0f235e0650eff296" 759 + dependencies = [ 760 + "http", 761 + "serde", 762 + "serde_html_form", 763 + "serde_json", 764 + "thiserror 1.0.69", 765 + "trait-variant", 766 + ] 767 + 768 + [[package]] 662 769 name = "attohttpc" 663 770 version = "0.30.1" 664 771 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 847 954 "cc", 848 955 "cfg-if", 849 956 "constant_time_eq", 850 - "cpufeatures", 957 + "cpufeatures 0.2.17", 851 958 ] 852 959 853 960 [[package]] ··· 997 1104 checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 998 1105 999 1106 [[package]] 1107 + name = "chacha20" 1108 + version = "0.10.0" 1109 + source = "registry+https://github.com/rust-lang/crates.io-index" 1110 + checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" 1111 + dependencies = [ 1112 + "cfg-if", 1113 + "cpufeatures 0.3.0", 1114 + "rand_core 0.10.0", 1115 + ] 1116 + 1117 + [[package]] 1000 1118 name = "chrono" 1001 1119 version = "0.4.44" 1002 1120 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1118 1236 ] 1119 1237 1120 1238 [[package]] 1239 + name = "compression-codecs" 1240 + version = "0.4.37" 1241 + source = "registry+https://github.com/rust-lang/crates.io-index" 1242 + checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" 1243 + dependencies = [ 1244 + "compression-core", 1245 + "flate2", 1246 + "memchr", 1247 + ] 1248 + 1249 + [[package]] 1250 + name = "compression-core" 1251 + version = "0.4.31" 1252 + source = "registry+https://github.com/rust-lang/crates.io-index" 1253 + checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" 1254 + 1255 + [[package]] 1121 1256 name = "concurrent-queue" 1122 1257 version = "2.5.0" 1123 1258 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1258 1393 ] 1259 1394 1260 1395 [[package]] 1396 + name = "cpufeatures" 1397 + version = "0.3.0" 1398 + source = "registry+https://github.com/rust-lang/crates.io-index" 1399 + checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" 1400 + dependencies = [ 1401 + "libc", 1402 + ] 1403 + 1404 + [[package]] 1261 1405 name = "crc32fast" 1262 1406 version = "1.5.0" 1263 1407 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1380 1524 checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" 1381 1525 dependencies = [ 1382 1526 "cfg-if", 1383 - "cpufeatures", 1527 + "cpufeatures 0.2.17", 1384 1528 "curve25519-dalek-derive", 1385 1529 "digest 0.10.7", 1386 1530 "fiat-crypto 0.2.9", ··· 1396 1540 checksum = "6f9200d1d13637f15a6acb71e758f64624048d85b31a5fdbfd8eca1e2687d0b7" 1397 1541 dependencies = [ 1398 1542 "cfg-if", 1399 - "cpufeatures", 1543 + "cpufeatures 0.2.17", 1400 1544 "curve25519-dalek-derive", 1401 1545 "digest 0.11.0-rc.10", 1402 1546 "fiat-crypto 0.3.0", ··· 1520 1664 "darling_core 0.23.0", 1521 1665 "quote", 1522 1666 "syn 2.0.117", 1667 + ] 1668 + 1669 + [[package]] 1670 + name = "dashmap" 1671 + version = "6.1.0" 1672 + source = "registry+https://github.com/rust-lang/crates.io-index" 1673 + checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" 1674 + dependencies = [ 1675 + "cfg-if", 1676 + "crossbeam-utils", 1677 + "hashbrown 0.14.5", 1678 + "lock_api", 1679 + "once_cell", 1680 + "parking_lot_core", 1523 1681 ] 1524 1682 1525 1683 [[package]] ··· 2653 2811 2654 2812 [[package]] 2655 2813 name = "hashbrown" 2814 + version = "0.14.5" 2815 + source = "registry+https://github.com/rust-lang/crates.io-index" 2816 + checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 2817 + 2818 + [[package]] 2819 + name = "hashbrown" 2656 2820 version = "0.15.5" 2657 2821 source = "registry+https://github.com/rust-lang/crates.io-index" 2658 2822 checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" ··· 2739 2903 ] 2740 2904 2741 2905 [[package]] 2906 + name = "hickory-net" 2907 + version = "0.26.0" 2908 + source = "registry+https://github.com/rust-lang/crates.io-index" 2909 + checksum = "0c61c8db47fae51ba9f8f2a2748bd87542acfbe22f2ec9cf9c8ec72d1ee6e9a6" 2910 + dependencies = [ 2911 + "async-trait", 2912 + "cfg-if", 2913 + "data-encoding", 2914 + "futures-channel", 2915 + "futures-io", 2916 + "futures-util", 2917 + "hickory-proto 0.26.0", 2918 + "idna", 2919 + "ipnet", 2920 + "jni 0.22.4", 2921 + "rand 0.10.0", 2922 + "thiserror 2.0.18", 2923 + "tinyvec", 2924 + "tokio", 2925 + "tracing", 2926 + "url", 2927 + ] 2928 + 2929 + [[package]] 2742 2930 name = "hickory-proto" 2743 2931 version = "0.25.2" 2744 2932 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2769 2957 ] 2770 2958 2771 2959 [[package]] 2960 + name = "hickory-proto" 2961 + version = "0.26.0" 2962 + source = "registry+https://github.com/rust-lang/crates.io-index" 2963 + checksum = "a916d0494600d99ecb15aadfab677ad97c4de559e8f1af0c129353a733ac1fcc" 2964 + dependencies = [ 2965 + "data-encoding", 2966 + "idna", 2967 + "ipnet", 2968 + "jni 0.22.4", 2969 + "once_cell", 2970 + "prefix-trie", 2971 + "rand 0.10.0", 2972 + "ring", 2973 + "thiserror 2.0.18", 2974 + "tinyvec", 2975 + "tracing", 2976 + "url", 2977 + ] 2978 + 2979 + [[package]] 2772 2980 name = "hickory-resolver" 2773 2981 version = "0.25.2" 2774 2982 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2776 2984 dependencies = [ 2777 2985 "cfg-if", 2778 2986 "futures-util", 2779 - "hickory-proto", 2987 + "hickory-proto 0.25.2", 2780 2988 "ipconfig", 2781 2989 "moka", 2782 2990 "once_cell", ··· 2788 2996 "thiserror 2.0.18", 2789 2997 "tokio", 2790 2998 "tokio-rustls", 2999 + "tracing", 3000 + ] 3001 + 3002 + [[package]] 3003 + name = "hickory-resolver" 3004 + version = "0.26.0" 3005 + source = "registry+https://github.com/rust-lang/crates.io-index" 3006 + checksum = "a10bd64d950b4d38ca21e25c8ae230712e4955fb8290cfcb29a5e5dc6017e544" 3007 + dependencies = [ 3008 + "cfg-if", 3009 + "futures-util", 3010 + "hickory-net", 3011 + "hickory-proto 0.26.0", 3012 + "ipconfig", 3013 + "ipnet", 3014 + "jni 0.22.4", 3015 + "moka", 3016 + "ndk-context", 3017 + "once_cell", 3018 + "parking_lot", 3019 + "rand 0.10.0", 3020 + "resolv-conf", 3021 + "smallvec", 3022 + "system-configuration", 3023 + "thiserror 2.0.18", 3024 + "tokio", 2791 3025 "tracing", 2792 3026 ] 2793 3027 ··· 3198 3432 version = "2.12.0" 3199 3433 source = "registry+https://github.com/rust-lang/crates.io-index" 3200 3434 checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" 3435 + dependencies = [ 3436 + "serde", 3437 + ] 3201 3438 3202 3439 [[package]] 3203 3440 name = "iri-string" ··· 3223 3460 "ed25519-dalek 3.0.0-pre.1", 3224 3461 "futures-util", 3225 3462 "getrandom 0.3.4", 3226 - "hickory-resolver", 3463 + "hickory-resolver 0.25.2", 3227 3464 "http", 3228 3465 "ipnet", 3229 3466 "iroh-base", ··· 3405 3642 "data-encoding", 3406 3643 "derive_more", 3407 3644 "getrandom 0.3.4", 3408 - "hickory-resolver", 3645 + "hickory-resolver 0.25.2", 3409 3646 "http", 3410 3647 "http-body-util", 3411 3648 "hyper", ··· 3549 3786 ] 3550 3787 3551 3788 [[package]] 3789 + name = "jni" 3790 + version = "0.22.4" 3791 + source = "registry+https://github.com/rust-lang/crates.io-index" 3792 + checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" 3793 + dependencies = [ 3794 + "cfg-if", 3795 + "combine", 3796 + "jni-macros", 3797 + "jni-sys 0.4.1", 3798 + "log", 3799 + "simd_cesu8", 3800 + "thiserror 2.0.18", 3801 + "walkdir", 3802 + "windows-link", 3803 + ] 3804 + 3805 + [[package]] 3806 + name = "jni-macros" 3807 + version = "0.22.4" 3808 + source = "registry+https://github.com/rust-lang/crates.io-index" 3809 + checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" 3810 + dependencies = [ 3811 + "proc-macro2", 3812 + "quote", 3813 + "rustc_version", 3814 + "simd_cesu8", 3815 + "syn 2.0.117", 3816 + ] 3817 + 3818 + [[package]] 3552 3819 name = "jni-sys" 3553 3820 version = "0.3.1" 3554 3821 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3587 3854 ] 3588 3855 3589 3856 [[package]] 3857 + name = "jose-b64" 3858 + version = "0.1.2" 3859 + source = "registry+https://github.com/rust-lang/crates.io-index" 3860 + checksum = "bec69375368709666b21c76965ce67549f2d2db7605f1f8707d17c9656801b56" 3861 + dependencies = [ 3862 + "base64ct", 3863 + "serde", 3864 + "subtle", 3865 + "zeroize", 3866 + ] 3867 + 3868 + [[package]] 3869 + name = "jose-jwa" 3870 + version = "0.1.2" 3871 + source = "registry+https://github.com/rust-lang/crates.io-index" 3872 + checksum = "9ab78e053fe886a351d67cf0d194c000f9d0dcb92906eb34d853d7e758a4b3a7" 3873 + dependencies = [ 3874 + "serde", 3875 + ] 3876 + 3877 + [[package]] 3878 + name = "jose-jwk" 3879 + version = "0.1.2" 3880 + source = "registry+https://github.com/rust-lang/crates.io-index" 3881 + checksum = "280fa263807fe0782ecb6f2baadc28dffc04e00558a58e33bfdb801d11fd58e7" 3882 + dependencies = [ 3883 + "jose-b64", 3884 + "jose-jwa", 3885 + "p256", 3886 + "serde", 3887 + "zeroize", 3888 + ] 3889 + 3890 + [[package]] 3590 3891 name = "js-sys" 3591 3892 version = "0.3.91" 3592 3893 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3639 3940 checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" 3640 3941 dependencies = [ 3641 3942 "log", 3943 + ] 3944 + 3945 + [[package]] 3946 + name = "langtag" 3947 + version = "0.3.4" 3948 + source = "registry+https://github.com/rust-lang/crates.io-index" 3949 + checksum = "ed60c85f254d6ae8450cec15eedd921efbc4d1bdf6fcf6202b9a58b403f6f805" 3950 + dependencies = [ 3951 + "serde", 3642 3952 ] 3643 3953 3644 3954 [[package]] ··· 4002 4312 source = "registry+https://github.com/rust-lang/crates.io-index" 4003 4313 checksum = "85f8024e1c8e71c778968af91d43700ce1d11b219d127d79fb2934153b82b42b" 4004 4314 dependencies = [ 4315 + "async-lock", 4005 4316 "crossbeam-channel", 4006 4317 "crossbeam-epoch", 4007 4318 "crossbeam-utils", 4008 4319 "equivalent", 4320 + "event-listener 5.4.1", 4321 + "futures-util", 4009 4322 "parking_lot", 4010 4323 "portable-atomic", 4011 4324 "smallvec", ··· 4124 4437 "security-framework-sys", 4125 4438 "tempfile", 4126 4439 ] 4440 + 4441 + [[package]] 4442 + name = "ndk-context" 4443 + version = "0.1.1" 4444 + source = "registry+https://github.com/rust-lang/crates.io-index" 4445 + checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" 4127 4446 4128 4447 [[package]] 4129 4448 name = "nested_enum_utils" ··· 4944 5263 checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" 4945 5264 dependencies = [ 4946 5265 "cfg-if", 4947 - "cpufeatures", 5266 + "cpufeatures 0.2.17", 4948 5267 "opaque-debug", 4949 5268 "universal-hash", 4950 5269 ] ··· 5057 5376 ] 5058 5377 5059 5378 [[package]] 5379 + name = "prefix-trie" 5380 + version = "0.8.2" 5381 + source = "registry+https://github.com/rust-lang/crates.io-index" 5382 + checksum = "23370be78b7e5bcbb0cab4a02047eb040279a693c78daad04c2c5f1c24a83503" 5383 + dependencies = [ 5384 + "either", 5385 + "ipnet", 5386 + "num-traits", 5387 + ] 5388 + 5389 + [[package]] 5060 5390 name = "prettyplease" 5061 5391 version = "0.2.37" 5062 5392 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5258 5588 source = "registry+https://github.com/rust-lang/crates.io-index" 5259 5589 checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" 5260 5590 dependencies = [ 5591 + "chacha20", 5261 5592 "getrandom 0.4.2", 5262 5593 "rand_core 0.10.0", 5263 5594 ] ··· 5727 6058 dependencies = [ 5728 6059 "core-foundation 0.10.1", 5729 6060 "core-foundation-sys", 5730 - "jni", 6061 + "jni 0.21.1", 5731 6062 "log", 5732 6063 "once_cell", 5733 6064 "rustls", ··· 6007 6338 ] 6008 6339 6009 6340 [[package]] 6341 + name = "serde_html_form" 6342 + version = "0.2.8" 6343 + source = "registry+https://github.com/rust-lang/crates.io-index" 6344 + checksum = "b2f2d7ff8a2140333718bb329f5c40fc5f0865b84c426183ce14c97d2ab8154f" 6345 + dependencies = [ 6346 + "form_urlencoded", 6347 + "indexmap", 6348 + "itoa", 6349 + "ryu", 6350 + "serde_core", 6351 + ] 6352 + 6353 + [[package]] 6010 6354 name = "serde_ipld_dagcbor" 6011 6355 version = "0.6.4" 6012 6356 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 6097 6441 checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" 6098 6442 dependencies = [ 6099 6443 "cfg-if", 6100 - "cpufeatures", 6444 + "cpufeatures 0.2.17", 6101 6445 "digest 0.10.7", 6102 6446 ] 6103 6447 ··· 6108 6452 checksum = "d1e3878ab0f98e35b2df35fe53201d088299b41a6bb63e3e34dada2ac4abd924" 6109 6453 dependencies = [ 6110 6454 "cfg-if", 6111 - "cpufeatures", 6455 + "cpufeatures 0.2.17", 6112 6456 "digest 0.11.0-rc.10", 6113 6457 ] 6114 6458 ··· 6160 6504 checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" 6161 6505 6162 6506 [[package]] 6507 + name = "simd_cesu8" 6508 + version = "1.1.1" 6509 + source = "registry+https://github.com/rust-lang/crates.io-index" 6510 + checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" 6511 + dependencies = [ 6512 + "rustc_version", 6513 + "simdutf8", 6514 + ] 6515 + 6516 + [[package]] 6163 6517 name = "simdutf8" 6164 6518 version = "0.1.5" 6165 6519 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 6360 6714 checksum = "1a5ab62937edac8b23fa40e55a358ea1924245b17fc1eb20d14929c8f11be98d" 6361 6715 dependencies = [ 6362 6716 "acto", 6363 - "hickory-proto", 6717 + "hickory-proto 0.25.2", 6364 6718 "rand 0.9.2", 6365 6719 "socket2 0.6.3", 6366 6720 "thiserror 2.0.18", ··· 6554 6908 dependencies = [ 6555 6909 "anyhow", 6556 6910 "async-std", 6911 + "atrium-api", 6912 + "atrium-common", 6913 + "atrium-identity", 6914 + "atrium-oauth", 6557 6915 "axum", 6558 6916 "axum-macros", 6559 6917 "clap", ··· 6561 6919 "env_logger", 6562 6920 "futures-util", 6563 6921 "hf-hub", 6922 + "hickory-resolver 0.26.0", 6564 6923 "iroh", 6565 6924 "iroh-blobs", 6566 6925 "iroh-gossip", ··· 6847 7206 source = "registry+https://github.com/rust-lang/crates.io-index" 6848 7207 checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" 6849 7208 dependencies = [ 7209 + "async-compression", 6850 7210 "bitflags", 6851 7211 "bytes", 7212 + "futures-core", 6852 7213 "futures-util", 6853 7214 "http", 6854 7215 "http-body", 7216 + "http-body-util", 6855 7217 "iri-string", 6856 7218 "pin-project-lite", 7219 + "tokio", 7220 + "tokio-util", 6857 7221 "tower", 6858 7222 "tower-layer", 6859 7223 "tower-service", ··· 6943 7307 "tracing", 6944 7308 "tracing-core", 6945 7309 "tracing-log", 7310 + ] 7311 + 7312 + [[package]] 7313 + name = "trait-variant" 7314 + version = "0.1.2" 7315 + source = "registry+https://github.com/rust-lang/crates.io-index" 7316 + checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" 7317 + dependencies = [ 7318 + "proc-macro2", 7319 + "quote", 7320 + "syn 2.0.117", 6946 7321 ] 6947 7322 6948 7323 [[package]] ··· 8135 8510 source = "registry+https://github.com/rust-lang/crates.io-index" 8136 8511 checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" 8137 8512 dependencies = [ 8513 + "serde", 8138 8514 "zeroize_derive", 8139 8515 ] 8140 8516
+5
tiles/Cargo.toml
··· 31 31 iroh-blobs = "0.99.0" 32 32 log = "0.4.29" 33 33 env_logger = "0.11.10" 34 + atrium-oauth = "0.1.7" 35 + atrium-identity = "0.1.9" 36 + hickory-resolver = "0.26.0" 37 + atrium-common = "0.1.4" 38 + atrium-api = "0.25.8" 34 39 35 40 [dev-dependencies] 36 41 tempfile = "3"
+1 -1
tiles/src/commands/mod.rs
··· 4 4 5 5 use anyhow::{Result, anyhow}; 6 6 use owo_colors::OwoColorize; 7 - use tiles::core::accounts::{ 7 + use tiles::core::account::local::{ 8 8 RootUser, create_root_account, get_peer_list, get_root_user_details, save_root_account, 9 9 set_nickname, unlink, 10 10 };
+409
tiles/src/core/account/atproto.rs
··· 1 + //! Handles atprotocol stuff 2 + 3 + use anyhow::{Result, anyhow}; 4 + use atrium_api::types::string::Did; 5 + use atrium_common::store::Store; 6 + use atrium_identity::{ 7 + did::{CommonDidResolver, CommonDidResolverConfig, DEFAULT_PLC_DIRECTORY_URL}, 8 + handle::{AtprotoHandleResolver, AtprotoHandleResolverConfig, DnsTxtResolver}, 9 + }; 10 + use atrium_oauth::{ 11 + AtprotoLocalhostClientMetadata, AuthorizeOptions, CallbackParams, DefaultHttpClient, 12 + KnownScope, OAuthClient, OAuthClientConfig, OAuthResolverConfig, Scope, 13 + store::{session::MemorySessionStore, state::MemoryStateStore}, 14 + }; 15 + use log::info; 16 + use reqwest::Client; 17 + use rusqlite::{Connection, OptionalExtension, params}; 18 + use serde::{Deserialize, Serialize}; 19 + use std::{fmt::Debug, process::Command, sync::Arc, time::Duration}; 20 + use tokio::sync::oneshot; 21 + 22 + use std::error::Error; 23 + 24 + use hickory_resolver::TokioResolver; 25 + 26 + use crate::{core::storage::db::Dbconn, daemon::start_internal_server, utils::get_unix_time_now}; 27 + 28 + #[derive(Deserialize)] 29 + struct HandleResolve { 30 + did: String, 31 + } 32 + 33 + #[derive(Deserialize, Debug, Serialize)] 34 + pub struct AtCallbackParams { 35 + code: Option<String>, 36 + iss: Option<String>, 37 + state: Option<String>, 38 + error: Option<String>, 39 + error_description: Option<String>, 40 + } 41 + 42 + struct AtprotoAuthData { 43 + // did:plc 44 + key: String, 45 + // serialized session data 46 + session: String, 47 + // serialized state data 48 + state: String, 49 + #[allow(dead_code)] 50 + is_logged_in: bool, 51 + created_at: u64, 52 + updated_at: u64, 53 + #[allow(dead_code)] 54 + handle: String, 55 + } 56 + 57 + struct HickoryDnsTxtResolver { 58 + resolver: TokioResolver, 59 + } 60 + 61 + impl Default for HickoryDnsTxtResolver { 62 + fn default() -> Self { 63 + Self { 64 + resolver: TokioResolver::builder_tokio() 65 + .expect("Failed to create TokioResolver builder") 66 + .build() 67 + .expect("Failed to build tokio resolver"), 68 + } 69 + } 70 + } 71 + 72 + impl DnsTxtResolver for HickoryDnsTxtResolver { 73 + async fn resolve( 74 + &self, 75 + query: &str, 76 + ) -> core::result::Result<Vec<String>, Box<dyn Error + Send + Sync + 'static>> { 77 + Ok(self 78 + .resolver 79 + .txt_lookup(query) 80 + .await? 81 + .answers() 82 + .iter() 83 + .map(|txt| txt.to_string()) 84 + .collect()) 85 + } 86 + } 87 + 88 + pub async fn login(conn: &Dbconn, handle: &str) -> Result<()> { 89 + let http_client = Arc::new(DefaultHttpClient::default()); 90 + const LOGIN_PORT: u32 = 8988; 91 + 92 + let mem_session_store = MemorySessionStore::default(); 93 + let mem_state_store = MemoryStateStore::default(); 94 + 95 + let config = OAuthClientConfig { 96 + client_metadata: AtprotoLocalhostClientMetadata { 97 + redirect_uris: Some(vec![String::from("http://127.0.0.1:8988/callback")]), 98 + scopes: Some(vec![ 99 + Scope::Known(KnownScope::Atproto), 100 + Scope::Known(KnownScope::TransitionGeneric), 101 + ]), 102 + }, 103 + keys: None, 104 + resolver: OAuthResolverConfig { 105 + did_resolver: CommonDidResolver::new(CommonDidResolverConfig { 106 + plc_directory_url: DEFAULT_PLC_DIRECTORY_URL.to_string(), 107 + http_client: http_client.clone(), 108 + }), 109 + handle_resolver: AtprotoHandleResolver::new(AtprotoHandleResolverConfig { 110 + dns_txt_resolver: HickoryDnsTxtResolver::default(), 111 + http_client: http_client.clone(), 112 + }), 113 + authorization_server_metadata: Default::default(), 114 + protected_resource_metadata: Default::default(), 115 + }, 116 + state_store: mem_state_store.clone(), 117 + session_store: mem_session_store.clone(), 118 + }; 119 + 120 + let Ok(client) = OAuthClient::new(config) else { 121 + panic!("client fuck up") 122 + }; 123 + 124 + //TODO: This resolve function is hack to convert handle to DID 125 + // cuz for some reason the authorize fn not working for customd domains 126 + // it does work for bluesky hosted handles and DIDs. 127 + // Probably smthng to do w DNS resolver. Will dig more latta 128 + let did = resolve_handle_to_did(handle) 129 + .await 130 + .inspect_err(|_| eprintln!("Failed to resolve handle"))?; 131 + 132 + info!("{}", did); 133 + let url = client 134 + .authorize( 135 + did.clone(), 136 + AuthorizeOptions { 137 + scopes: vec![ 138 + Scope::Known(KnownScope::Atproto), 139 + Scope::Known(KnownScope::TransitionGeneric), 140 + ], 141 + ..Default::default() 142 + }, 143 + ) 144 + .await 145 + .inspect_err(|_| eprintln!("Failed to authorize"))?; 146 + 147 + let mut child = Command::new("open").arg(url).spawn()?; 148 + child.wait()?; 149 + let (callback_tx, callback_rx) = oneshot::channel(); 150 + 151 + //TODO: can we randomze port 152 + start_internal_server(Some(LOGIN_PORT), callback_tx).await?; 153 + let params = callback_rx.await?; 154 + info!("params recieved {:?}", params); 155 + 156 + if let Some(code) = params.code { 157 + let cb_params = CallbackParams { 158 + code, 159 + state: params.state, 160 + iss: params.iss, 161 + }; 162 + let (_auth_session, _) = client.callback(cb_params).await?; 163 + 164 + let did_struct = Did::new(did.clone()).map_err(|_e| anyhow!("Failed to convert to Did"))?; 165 + 166 + let session = mem_session_store 167 + .get(&did_struct) 168 + .await? 169 + .expect("Expected Session"); 170 + let session_string = serde_json::to_string(&session)?; 171 + 172 + let auth_data = AtprotoAuthData { 173 + key: did.clone(), 174 + session: session_string, 175 + state: "".to_owned(), 176 + is_logged_in: true, 177 + created_at: get_unix_time_now(), 178 + updated_at: get_unix_time_now(), 179 + handle: handle.to_owned(), 180 + }; 181 + 182 + upsert_auth_data(&conn.common, &auth_data)?; 183 + println!("LoggedIn successfully as {}", handle); 184 + } else { 185 + eprintln!( 186 + "Error authorizing due to {}", 187 + params 188 + .error_description 189 + .unwrap_or("unknow reason".to_owned()) 190 + ); 191 + } 192 + Ok(()) 193 + } 194 + 195 + pub fn logout(conn: &Dbconn) -> Result<()> { 196 + if let Some(auth_user) = fetch_logged_in_data(&conn.common)? { 197 + let key = auth_user.key.clone(); 198 + let logout_user = AtprotoAuthData { 199 + is_logged_in: false, 200 + ..auth_user 201 + }; 202 + upsert_auth_data(&conn.common, &logout_user)?; 203 + println!("Loggedout successfully as {}", key); 204 + } else { 205 + println!("No logged-in user, please login") 206 + } 207 + Ok(()) 208 + } 209 + 210 + async fn resolve_handle_to_did(handle: &str) -> Result<String> { 211 + let client_builder = Client::builder().timeout(Duration::from_secs(5)); 212 + let client = client_builder.build()?; 213 + let response = client 214 + .get(format!( 215 + "https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle={}", 216 + handle 217 + )) 218 + .send() 219 + .await; 220 + 221 + match response { 222 + Err(err) if err.is_timeout() => Err(anyhow!("Request failed due to Api timedout")), 223 + Err(err) => Err(anyhow!("Request failed due to {:?}", err)), 224 + Ok(res) if res.status() == 200 => { 225 + let resolve_data = res.json::<HandleResolve>().await?; 226 + Ok(resolve_data.did) 227 + } 228 + Ok(res) => Err(anyhow!("Api failed with status {}", res.status())), 229 + } 230 + } 231 + 232 + fn upsert_auth_data(conn: &Connection, data: &AtprotoAuthData) -> Result<()> { 233 + let mut stmt = conn.prepare( 234 + "insert into atproto_auth_data(key, session, state, is_logged_in, created_at, updated_at, handle) values (?1, ?2, ?3, ?4, ?5, ?6, ?7) 235 + on conflict(key) 236 + do update set session = ?2, updated_at = ?6, is_logged_in = ?4 237 + ", 238 + )?; 239 + 240 + match stmt.execute(params![ 241 + data.key.to_owned(), 242 + data.session.to_owned(), 243 + data.state.to_owned(), 244 + data.is_logged_in, 245 + data.created_at as f64, 246 + data.updated_at as f64, 247 + data.handle.to_owned() 248 + ]) { 249 + Ok(_res) => Ok(()), 250 + Err(err) => Err(anyhow!("Err inserting due to {}", err)), 251 + } 252 + } 253 + 254 + #[allow(dead_code)] 255 + fn fetch_auth_data(conn: &Connection, did: &str) -> Result<AtprotoAuthData> { 256 + let data = conn.query_row( 257 + "SELECT key, session, state, is_logged_in, created_at , updated_at, handle FROM atproto_auth_data WHERE key = ?1", 258 + [did], 259 + |row| { 260 + Ok(AtprotoAuthData { 261 + key: row.get(0)?, 262 + session: row.get(1)?, 263 + state: row.get(2)?, 264 + is_logged_in: row.get(3)?, 265 + created_at: row.get::<usize, f64>(4)? as u64, 266 + updated_at: row.get::<usize, f64>(5)? as u64, 267 + handle: row.get(6)?, 268 + }) 269 + }, 270 + )?; 271 + Ok(data) 272 + } 273 + 274 + #[allow(dead_code)] 275 + fn delete_auth_data(conn: &Connection, did: &str) -> Result<()> { 276 + let mut stmt = conn.prepare("delete from atproto_auth_data where key = ?1")?; 277 + 278 + match stmt.execute(params![did]) { 279 + Ok(_res) => Ok(()), 280 + Err(err) => Err(anyhow!("Err deleting due to {}", err)), 281 + } 282 + } 283 + 284 + fn fetch_logged_in_data(conn: &Connection) -> Result<Option<AtprotoAuthData>> { 285 + conn.query_row( 286 + "SELECT key, session, state, is_logged_in, created_at, updated_at, handle FROM atproto_auth_data WHERE is_logged_in = true", 287 + [], 288 + |row| { 289 + Ok(AtprotoAuthData { 290 + key: row.get(0)?, 291 + session: row.get(1)?, 292 + state: row.get(2)?, 293 + is_logged_in: row.get(3)?, 294 + created_at: row.get::<usize, f64>(4)? as u64, 295 + updated_at: row.get::<usize, f64>(5)? as u64, 296 + handle: row.get(6)?, 297 + }) 298 + }, 299 + ).optional().map_err(Into::<anyhow::Error>::into) 300 + } 301 + 302 + #[cfg(test)] 303 + mod tests { 304 + use super::*; 305 + use rusqlite::Connection; 306 + 307 + #[test] 308 + fn test_add_a_new_auth_entry() { 309 + let conn = setup_db_schema(); 310 + 311 + let auth_data = AtprotoAuthData { 312 + key: String::from("did:plc:wth"), 313 + session: String::from("session_stuff"), 314 + state: "".to_owned(), 315 + is_logged_in: true, 316 + created_at: get_unix_time_now(), 317 + updated_at: get_unix_time_now(), 318 + handle: "madcla.ws".to_owned(), 319 + }; 320 + 321 + upsert_auth_data(&conn, &auth_data).unwrap(); 322 + let auth_data_2 = fetch_auth_data(&conn, "did:plc:wth").unwrap(); 323 + 324 + assert_eq!(auth_data.key, auth_data_2.key) 325 + } 326 + 327 + #[test] 328 + fn test_add_same_auth_entry() { 329 + let conn = setup_db_schema(); 330 + 331 + let auth_data = AtprotoAuthData { 332 + key: String::from("did:plc:wth"), 333 + session: String::from("session_stuff"), 334 + state: "".to_owned(), 335 + is_logged_in: true, 336 + created_at: get_unix_time_now(), 337 + updated_at: get_unix_time_now(), 338 + handle: "madcla.ws".to_owned(), 339 + }; 340 + 341 + upsert_auth_data(&conn, &auth_data).unwrap(); 342 + let auth_data_2 = fetch_auth_data(&conn, "did:plc:wth").unwrap(); 343 + 344 + assert_eq!(auth_data.key, auth_data_2.key); 345 + 346 + let auth_data_2 = AtprotoAuthData { 347 + key: String::from("did:plc:wth"), 348 + session: String::from("session_stuff_2"), 349 + state: "".to_owned(), 350 + is_logged_in: true, 351 + created_at: get_unix_time_now(), 352 + updated_at: get_unix_time_now(), 353 + handle: "madcla.ws".to_owned(), 354 + }; 355 + 356 + upsert_auth_data(&conn, &auth_data_2).unwrap(); 357 + 358 + let auth_data_2 = fetch_auth_data(&conn, "did:plc:wth").unwrap(); 359 + 360 + assert_eq!(auth_data_2.session, "session_stuff_2"); 361 + } 362 + 363 + #[test] 364 + fn test_fetch_valid_logged_in_auth_entry() { 365 + let conn = setup_db_schema(); 366 + 367 + let auth_data = AtprotoAuthData { 368 + key: String::from("did:plc:wth"), 369 + session: String::from("session_stuff"), 370 + state: "".to_owned(), 371 + is_logged_in: true, 372 + created_at: get_unix_time_now(), 373 + updated_at: get_unix_time_now(), 374 + handle: "madcla.ws".to_owned(), 375 + }; 376 + 377 + upsert_auth_data(&conn, &auth_data).unwrap(); 378 + let auth_data_2 = fetch_logged_in_data(&conn).unwrap(); 379 + 380 + assert!(auth_data_2.is_some()) 381 + } 382 + 383 + #[test] 384 + fn test_fetch_zero_logged_in_auth_entry() { 385 + let conn = setup_db_schema(); 386 + 387 + let auth_data_2 = fetch_logged_in_data(&conn).unwrap(); 388 + 389 + assert!(auth_data_2.is_none()) 390 + } 391 + fn setup_db_schema() -> Connection { 392 + let conn = Connection::open_in_memory().unwrap(); 393 + conn.execute( 394 + "CREATE TABLE IF NOT EXISTS atproto_auth_data( 395 + key TEXT PRIMARY KEY, 396 + session TEXT , 397 + state TEXT, 398 + is_logged_in INTEGER NOT NULL, 399 + created_at INTEGER NOT NULL, 400 + updated_at INTEGER NOT NULL, 401 + handle TEXT NOT NULL 402 + )", 403 + [], 404 + ) 405 + .unwrap(); 406 + 407 + conn 408 + } 409 + }
+2
tiles/src/core/account/mod.rs
··· 1 + pub mod atproto; 2 + pub mod local;
+2 -2
tiles/src/core/accounts.rs tiles/src/core/account/local.rs
··· 1 - //! Accounts 1 + //! Local Account 2 2 // Stuff related to account and identity system 3 3 use anyhow::{Result, anyhow}; 4 4 use iroh::SecretKey; ··· 427 427 #[cfg(test)] 428 428 mod tests { 429 429 use super::*; 430 - use crate::core::accounts::{ 430 + use crate::core::account::local::{ 431 431 RootUser, create_root_account, get_current_user, get_root_user_details, set_nickname, 432 432 }; 433 433 use anyhow::Result;
+2 -2
tiles/src/core/chats.rs
··· 6 6 use std::collections::HashMap; 7 7 use std::str::FromStr; 8 8 9 - use crate::core::accounts::User; 9 + use crate::core::account::local::User; 10 10 use crate::core::storage::db::get_db_conn; 11 11 use crate::runtime::mlx::ChatResponse; 12 12 use crate::utils::get_unix_time_now; ··· 382 382 383 383 use crate::{ 384 384 core::{ 385 - accounts::{ACCOUNT, User}, 385 + account::local::{ACCOUNT, User}, 386 386 chats::{ 387 387 apply_delta, create_session, decode_delta_from_bytes, encode_delta_to_bytes, 388 388 get_delta, get_last_row_counter, save_chat,
+2 -2
tiles/src/core/mod.rs
··· 7 7 8 8 use crate::{ 9 9 core::{ 10 - accounts::save_root_account_db, 10 + account::local::save_root_account_db, 11 11 storage::db::{Dbconn, init_db}, 12 12 }, 13 13 utils::config::{ConfigProvider, DefaultProvider}, 14 14 }; 15 15 16 - pub mod accounts; 16 + pub mod account; 17 17 pub mod chats; 18 18 pub mod health; 19 19 pub mod network;
+13 -10
tiles/src/core/network/mod.rs
··· 40 40 use uuid::Uuid; 41 41 42 42 use crate::core::{ 43 - accounts::{ 44 - self, create_dummy_user, get_app_secret_key, get_current_user, get_user_info, 45 - save_peer_account_db, 43 + account::{ 44 + self, 45 + local::{ 46 + create_dummy_user, get_app_secret_key, get_current_user, get_user_info, 47 + save_peer_account_db, 48 + }, 46 49 }, 47 50 chats::{SyncOp, create_sync_channel}, 48 51 network::ticket::{EndpointUserData, LinkTicket}, ··· 66 69 } 67 70 68 71 impl NetworkMessage { 69 - fn new(user: &accounts::User, is_online: bool, body: MessageBody) -> Self { 72 + fn new(user: &account::local::User, is_online: bool, body: MessageBody) -> Self { 70 73 Self { 71 74 from_did: user.user_id.clone(), 72 75 from_nickname: user.username.clone(), ··· 228 231 async fn subsribe_loop( 229 232 mut receiver: GossipReceiver, 230 233 sender: GossipSender, 231 - user: accounts::User, 234 + user: account::local::User, 232 235 db_conn: Connection, 233 236 generated_ticket: Option<String>, 234 237 link_main_sender: tokio::sync::mpsc::Sender<u8>, ··· 343 346 async fn sync_subscribe_loop( 344 347 mut receiver: GossipReceiver, 345 348 sender: GossipSender, 346 - user: accounts::User, 349 + user: account::local::User, 347 350 store: MemStore, 348 351 endpoint: Endpoint, 349 352 sync_db_channel_sender: tokio::sync::mpsc::Sender<SyncOp>, ··· 405 408 } 406 409 Ok(()) 407 410 } 408 - async fn create_endpoint(user: &accounts::User) -> Result<Endpoint> { 411 + async fn create_endpoint(user: &account::local::User) -> Result<Endpoint> { 409 412 // In release mode, we will build the endpoint using 410 413 // tiles keypair in keychain 411 414 let usr_data = EndpointUserData::new(&user.user_id, &user.username); ··· 697 700 store: &MemStore, 698 701 msg: &NetworkMessage, 699 702 delivered_from: PublicKey, 700 - user: &accounts::User, 703 + user: &account::local::User, 701 704 sync_db_channel_sender: &tokio::sync::mpsc::Sender<SyncOp>, 702 705 ) -> Result<()> { 703 706 if let MessageBody::SyncStart { ··· 736 739 store: &MemStore, 737 740 msg: &NetworkMessage, 738 741 delivered_from: PublicKey, 739 - user: &accounts::User, 742 + user: &account::local::User, 740 743 endpoint: &Endpoint, 741 744 senders: ( 742 745 &tokio::sync::mpsc::Sender<SyncOp>, ··· 815 818 use tokio::sync::mpsc; 816 819 817 820 use crate::core::{ 818 - accounts::create_dummy_user, 821 + account::local::create_dummy_user, 819 822 chats::SyncOp, 820 823 network::{ 821 824 create_topic_id, fetch_last_row_counter, parse_link_ticket,
+27 -15
tiles/src/core/storage/db.rs
··· 26 26 27 27 // DEFINE MIGRATIONS 28 28 29 - const COMMON_MIGRATION_ARRAY: &[M] = &[M::up( 30 - " 31 - CREATE TABLE IF NOT EXISTS users ( 32 - id TEXT PRIMARY KEY, 33 - user_id TEXT NOT NULL, 34 - username TEXT NOT NULL, 35 - active_profile INTEGER NOT NULL DEFAULT 0 CHECK (active_profile IN (0,1)), 36 - account_type TEXT NOT NULL, 37 - root INTEGER NOT NULL DEFAULT 0 CHECK (root IN (0,1)), 38 - created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), 39 - updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), 40 - UNIQUE(account_type, user_id) 41 - ); 42 - ", 43 - )]; 29 + const COMMON_MIGRATION_ARRAY: &[M] = &[ 30 + M::up( 31 + "CREATE TABLE IF NOT EXISTS users ( 32 + id TEXT PRIMARY KEY, 33 + user_id TEXT NOT NULL, 34 + username TEXT NOT NULL, 35 + active_profile INTEGER NOT NULL DEFAULT 0 CHECK (active_profile IN (0,1)), 36 + account_type TEXT NOT NULL, 37 + root INTEGER NOT NULL DEFAULT 0 CHECK (root IN (0,1)), 38 + created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), 39 + updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now')), 40 + UNIQUE(account_type, user_id) 41 + ); 42 + ", 43 + ), 44 + M::up( 45 + "CREATE TABLE IF NOT EXISTS atproto_auth_data( 46 + key TEXT PRIMARY KEY, 47 + session TEXT , 48 + state TEXT, 49 + is_logged_in INTEGER NOT NULL, 50 + created_at INTEGER NOT NULL, 51 + updated_at INTEGER NOT NULL, 52 + handle TEXT NOT NULL 53 + )", 54 + ), 55 + ]; 44 56 45 57 const COMMON_MIGRATIONS: Migrations = Migrations::from_slice(COMMON_MIGRATION_ARRAY); 46 58
+53 -2
tiles/src/daemon.rs
··· 19 19 use semver::Version; 20 20 use std::fs::OpenOptions; 21 21 use std::sync::Mutex; 22 - use tokio::sync::oneshot::{self, Receiver}; 22 + use tokio::sync::oneshot::{self, Receiver, Sender}; 23 23 24 - use crate::utils::config::{ConfigProvider, DefaultProvider, get_model_cache}; 24 + use crate::{ 25 + core::account::atproto::AtCallbackParams, 26 + utils::config::{ConfigProvider, DefaultProvider, get_model_cache}, 27 + }; 25 28 26 29 struct AppState { 27 30 pub shutdown_sender: Mutex<Option<oneshot::Sender<bool>>>, 28 31 pub vsn: String, 32 + } 33 + 34 + struct InternalAppState { 35 + pub callback_sender: Mutex<Option<oneshot::Sender<AtCallbackParams>>>, 36 + pub shutdown_sender: Mutex<Option<oneshot::Sender<bool>>>, 29 37 } 30 38 31 39 #[derive(serde::Deserialize)] ··· 130 138 Ok(()) 131 139 } 132 140 141 + pub async fn start_internal_server( 142 + port: Option<u32>, 143 + callback_tx: Sender<AtCallbackParams>, 144 + ) -> Result<()> { 145 + let dyn_port: u32 = get_port(port); 146 + 147 + let (shutdown_tx, shutdown_rx) = oneshot::channel::<bool>(); 148 + 149 + let state = InternalAppState { 150 + callback_sender: Mutex::new(Some(callback_tx)), 151 + shutdown_sender: Mutex::new(Some(shutdown_tx)), 152 + }; 153 + let shared_state = Arc::new(state); 154 + let app = Router::new() 155 + .route("/callback", get(callback)) 156 + .with_state(shared_state); 157 + 158 + let addr = format!("127.0.0.1:{}", dyn_port); 159 + let listener = tokio::net::TcpListener::bind(addr).await?; 160 + 161 + info!("Internal server started at {}", dyn_port); 162 + let _ = axum::serve(listener, app) 163 + .with_graceful_shutdown(shutdown_signal(shutdown_rx)) 164 + .await; 165 + 166 + Ok(()) 167 + } 168 + 133 169 async fn shutdown_signal(rx: Receiver<bool>) { 134 170 rx.await.expect("shutdown receiver paniced"); 135 171 } ··· 142 178 } 143 179 144 180 #[debug_handler] 181 + async fn callback( 182 + State(state): State<Arc<InternalAppState>>, 183 + Query(params): Query<AtCallbackParams>, 184 + ) -> &'static str { 185 + info!("callback reached {:?}", params); 186 + //TODO: refactor this shit 187 + let mut cal_sender = state.callback_sender.lock().unwrap(); 188 + let cal_sender = cal_sender.take().unwrap(); 189 + let _ = cal_sender.send(params); 190 + let mut sender = state.shutdown_sender.lock().unwrap(); 191 + let sender_real = sender.take().unwrap(); 192 + let _ = sender_real.send(true); 193 + "Processed your authorization request, You can close this page" 194 + } 195 + 145 196 async fn get_model_cache_path( 146 197 State(_state): State<Arc<AppState>>, 147 198 Query(params): Query<SendParams>,
+27
tiles/src/main.rs
··· 6 6 use tiles::{ 7 7 core::{ 8 8 self, 9 + account::atproto::{login, logout}, 9 10 network::{link, sync}, 10 11 }, 11 12 daemon::{start_cmd, start_server, stop_cmd}, ··· 77 78 /// The DID of the peer you want to sync 78 79 did: Option<String>, 79 80 }, 81 + 82 + /// Atproto related commands 83 + At(AtArgs), 80 84 } 81 85 82 86 #[derive(Debug, Args)] ··· 187 191 /// Start the daemon 188 192 ListPeers, 189 193 } 194 + 195 + #[derive(Debug, Args)] 196 + #[command(args_conflicts_with_subcommands = true)] 197 + #[command(flatten_help = true)] 198 + struct AtArgs { 199 + #[command(subcommand)] 200 + command: AtCommands, 201 + } 202 + 203 + #[derive(Debug, Subcommand)] 204 + enum AtCommands { 205 + /// Produce link ticket and wait or send link request with ticket 206 + Login { 207 + handle: String, 208 + }, 209 + Logout, 210 + } 190 211 #[tokio::main] 191 212 pub async fn main() -> Result<(), Box<dyn Error>> { 192 213 build_logger(); ··· 294 315 } 295 316 }, 296 317 Some(Commands::Sync { did }) => sync(did).await?, 318 + Some(Commands::At(at_args)) => match at_args.command { 319 + AtCommands::Login { handle } => { 320 + login(&db_conn, &handle).await?; 321 + } 322 + AtCommands::Logout => logout(&db_conn)?, 323 + }, 297 324 } 298 325 Ok(()) 299 326 }
+1 -1
tiles/src/runtime/mlx.rs
··· 1 - use crate::core::accounts::get_current_user; 1 + use crate::core::account::local::get_current_user; 2 2 use crate::core::chats::{Message, create_session, save_chat}; 3 3 use crate::core::storage::db::Dbconn; 4 4 use crate::runtime::RunArgs;