diff --git a/Cargo.lock b/Cargo.lock index 2386a68..da24d81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,14 +92,14 @@ dependencies = [ [[package]] name = "async-global-executor" -version = "2.0.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c290043c9a95b05d45e952fb6383c67bcb61471f60cfa21e890dba6654234f43" +checksum = "5262ed948da60dd8956c6c5aca4d4163593dddb7b32d73267c93dab7b2e98940" dependencies = [ "async-channel", "async-executor", "async-io", - "async-mutex", + "async-lock", "blocking", "futures-lite", "num_cpus", @@ -108,9 +108,9 @@ dependencies = [ [[package]] name = "async-io" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +checksum = "e5e18f61464ae81cde0a23e713ae8fd299580c54d697a35820cfd0625b8b0e07" dependencies = [ "concurrent-queue", "futures-lite", @@ -134,15 +134,6 @@ dependencies = [ "event-listener", ] -[[package]] -name = "async-mutex" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" -dependencies = [ - "event-listener", -] - [[package]] name = "async-process" version = "1.4.0" @@ -162,9 +153,9 @@ dependencies = [ [[package]] name = "async-std" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52580991739c5cdb36cde8b2a516371c0a3b70dda36d916cc08b82372916808c" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" dependencies = [ "async-attributes", "async-channel", @@ -181,7 +172,6 @@ dependencies = [ "kv-log-macro", "log", "memchr", - "num_cpus", "once_cell", "pin-project-lite", "pin-utils", @@ -239,9 +229,9 @@ checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9" [[package]] name = "async-trait" -version = "0.1.53" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6aa3524a2dfcf9fe180c51eae2b58738348d819517ceadf95789c51fff7600" +checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" dependencies = [ "proc-macro2", "quote", @@ -368,9 +358,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.9.1" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" [[package]] name = "byte-tools" @@ -429,7 +419,7 @@ dependencies = [ "libc", "num-integer", "num-traits", - "time 0.1.43", + "time 0.1.44", "winapi 0.3.9", ] @@ -487,7 +477,7 @@ dependencies = [ "rand", "sha2", "subtle", - "time 0.3.9", + "time 0.3.11", "version_check", ] @@ -511,26 +501,26 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" dependencies = [ "autocfg", "cfg-if 1.0.0", "crossbeam-utils", - "lazy_static", "memoffset", + "once_cell", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" dependencies = [ "cfg-if 1.0.0", - "lazy_static", + "once_cell", ] [[package]] @@ -631,6 +621,15 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + [[package]] name = "dirs-sys" version = "0.3.7" @@ -943,13 +942,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -970,9 +969,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "globset" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a" dependencies = [ "aho-corasick", "bstr", @@ -1006,7 +1005,8 @@ dependencies = [ [[package]] name = "golgi" -version = "0.1.1" +version = "0.2.4" +source = "git+https://git.coopcloud.tech/golgi-ssb/golgi.git#9981b64bb23e1dbb95e31cfa1c545d23bb4f40d2" dependencies = [ "async-std", "async-stream 0.3.3", @@ -1016,6 +1016,7 @@ dependencies = [ "kuska-handshake", "kuska-sodiumoxide", "kuska-ssb", + "log", "serde", "serde_json", "sha2", @@ -1036,15 +1037,15 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.1", + "tokio-util 0.7.3", "tracing", ] [[package]] name = "hashbrown" -version = "0.11.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" [[package]] name = "hermit-abi" @@ -1081,9 +1082,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff8670570af52249509a86f5e3e18a08c60b177071826898fde8997cf5f6bfbb" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", @@ -1092,9 +1093,9 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", "http", @@ -1127,9 +1128,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.18" +version = "0.14.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b26ae0a80afebe130861d90abf98e3814a4f28a4c6ffeb5ab8ebb2be311e0ef2" +checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f" dependencies = [ "bytes", "futures-channel", @@ -1169,9 +1170,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.8.1" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" dependencies = [ "autocfg", "hashbrown", @@ -1224,15 +1225,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "js-sys" -version = "0.3.57" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" dependencies = [ "wasm-bindgen", ] @@ -1273,13 +1274,13 @@ dependencies = [ [[package]] name = "kuska-ssb" -version = "0.4.0" -source = "git+https://github.com/Kuska-ssb/ssb#fb7062de606e7c9cae8dd4df402a122db46c1b77" +version = "0.4.1" +source = "git+https://github.com/Kuska-ssb/ssb#315c7e31aa6255d5657665ce9357034113d2b552" dependencies = [ "async-std", "async-stream 0.2.1", "base64 0.11.0", - "dirs", + "dirs 2.0.2", "futures", "get_if_addrs", "hex", @@ -1316,9 +1317,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libsodium-sys" @@ -1354,9 +1355,9 @@ dependencies = [ [[package]] name = "loom" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc5c7d328e32cc4954e8e01193d7f0ef5ab257b5090b70a964e099a36034309" +checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" dependencies = [ "cfg-if 1.0.0", "generator", @@ -1383,6 +1384,8 @@ dependencies = [ "serde", "serde_json", "sled", + "uri_encode", + "xdg", ] [[package]] @@ -1442,9 +1445,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" +checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" dependencies = [ "libc", "log", @@ -1492,7 +1495,7 @@ dependencies = [ "mime", "spin", "tokio", - "tokio-util 0.6.9", + "tokio-util 0.6.10", "version_check", ] @@ -1574,9 +1577,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" [[package]] name = "opaque-debug" @@ -1609,9 +1612,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", "parking_lot_core 0.9.3", @@ -1815,11 +1818,11 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "proc-macro2" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" +checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -1837,9 +1840,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.18" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" dependencies = [ "proc-macro2", ] @@ -1916,9 +1919,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.5" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ "aho-corasick", "memchr", @@ -1936,9 +1939,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "remove_dir_all" @@ -1969,7 +1972,7 @@ dependencies = [ "memchr", "multer", "num_cpus", - "parking_lot 0.12.0", + "parking_lot 0.12.1", "pin-project-lite", "rand", "ref-cast", @@ -1978,10 +1981,10 @@ dependencies = [ "serde", "state", "tempfile", - "time 0.3.9", + "time 0.3.11", "tokio", "tokio-stream", - "tokio-util 0.7.1", + "tokio-util 0.7.3", "ubyte", "version_check", "yansi", @@ -2005,16 +2008,14 @@ dependencies = [ [[package]] name = "rocket_dyn_templates" -version = "0.1.0-rc.1" +version = "0.1.0-rc.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83f1287ad8fa034410928297a91db37518d5c46d7cc7e1e1b4a77aec0cd8807" +checksum = "bab13df598440527c200f46fb944dc55d8d67a1818b617eb5a3981dcd8b63fd2" dependencies = [ "glob", "normpath", "notify", "rocket", - "serde", - "serde_json", "tera", ] @@ -2040,22 +2041,22 @@ dependencies = [ "smallvec", "stable-pattern", "state", - "time 0.3.9", + "time 0.3.11", "tokio", "uncased", ] [[package]] name = "rustversion" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" +checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" [[package]] name = "same-file" @@ -2144,9 +2145,9 @@ dependencies = [ [[package]] name = "signal-hook" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d" +checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" dependencies = [ "libc", "signal-hook-registry", @@ -2200,9 +2201,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2" [[package]] name = "socket2" @@ -2246,13 +2247,13 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] name = "syn" -version = "1.0.92" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -2271,9 +2272,9 @@ dependencies = [ [[package]] name = "tera" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3cac831b615c25bcef632d1cabf864fa05813baad3d526829db18eb70e8b58d" +checksum = "7c9783d6ff395ae80cf17ed9a25360e7ba37742a79fa8fddabb073c5c7c8856d" dependencies = [ "chrono", "chrono-tz", @@ -2331,19 +2332,20 @@ dependencies = [ [[package]] name = "time" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", + "wasi 0.10.0+wasi-snapshot-preview1", "winapi 0.3.9", ] [[package]] name = "time" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2702e08a7a860f005826c6815dcac101b19b5eb330c27fe4a5928fec1d20ddd" +checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217" dependencies = [ "itoa", "libc", @@ -2359,14 +2361,14 @@ checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" [[package]] name = "tokio" -version = "1.18.2" +version = "1.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4903bf0427cf68dddd5aa6a93220756f8be0c34fcfa9f5e6191e103e15a31395" +checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" dependencies = [ "bytes", "libc", "memchr", - "mio 0.8.3", + "mio 0.8.4", "num_cpus", "once_cell", "pin-project-lite", @@ -2378,9 +2380,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" dependencies = [ "proc-macro2", "quote", @@ -2389,9 +2391,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" dependencies = [ "futures-core", "pin-project-lite", @@ -2400,9 +2402,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.9" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ "bytes", "futures-core", @@ -2414,9 +2416,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0edfdeb067411dba2044da6d1cb2df793dd35add7888d73c16e3381ded401764" +checksum = "cc463cd8deddc3770d20f9852143d50bf6094e640b485cb2e189a2099085ff45" dependencies = [ "bytes", "futures-core", @@ -2437,15 +2439,15 @@ dependencies = [ [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ "cfg-if 1.0.0", "pin-project-lite", @@ -2466,11 +2468,11 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.26" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" dependencies = [ - "lazy_static", + "once_cell", "valuable", ] @@ -2517,9 +2519,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "ubyte" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42756bb9e708855de2f8a98195643dff31a97f0485d90d8467b39dc24be9e8fe" +checksum = "a58e29f263341a29bb79e14ad7fda5f63b1c7e48929bad4c685d7876b1d04e94" dependencies = [ "serde", ] @@ -2532,9 +2534,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "uncased" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0" +checksum = "09b01702b0fd0b3fadcf98e098780badda8742d4f4a7676615cad90e8ac73622" dependencies = [ "serde", "version_check", @@ -2590,6 +2592,12 @@ dependencies = [ "unic-common", ] +[[package]] +name = "unicode-ident" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" + [[package]] name = "unicode-xid" version = "0.2.3" @@ -2606,6 +2614,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "uri_encode" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34acb51c736f8784bdbca2692c3cc57213bfc1b329fd6eb7668d5e4af8f87ceb" + [[package]] name = "valuable" version = "0.1.0" @@ -2657,9 +2671,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasi" @@ -2669,9 +2683,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -2679,9 +2693,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" dependencies = [ "bumpalo", "lazy_static", @@ -2694,9 +2708,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.30" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f741de44b75e14c35df886aff5f1eb73aa114fa5d4d00dcd37b5e01259bf3b2" +checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -2706,9 +2720,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2716,9 +2730,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" dependencies = [ "proc-macro2", "quote", @@ -2729,15 +2743,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.80" +version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" +checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" [[package]] name = "web-sys" -version = "0.3.57" +version = "0.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +checksum = "2fed94beee57daf8dd7d51f2b15dc2bcde92d7a72304cdf662a4371008b71b90" dependencies = [ "js-sys", "wasm-bindgen", @@ -2848,6 +2862,15 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "xdg" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4583db5cbd4c4c0303df2d15af80f0539db703fa1c68802d4cbbd2dd0f88f6" +dependencies = [ + "dirs 4.0.0", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 45203ba..8cd0235 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,18 +2,24 @@ name = "lykin" version = "0.1.0" edition = "2021" +authors = ["glyph "] +readme = "README.md" +description = "Symbiosis of SSB, key-value store and web server." +repository = "https://git.coopcloud.tech/glyph/lykin" +keywords = ["scuttlebutt", "ssb", "decentralized", "peer-for-peer", "p4p"] [dependencies] async-std = "1.10" bincode = "1.3" chrono = "0.4" +uri_encode = "1" env_logger = "0.9" futures = "0.3" -#golgi = { git = "https://git.coopcloud.tech/golgi-ssb/golgi.git" } -golgi = { path = "../golgi" } +golgi = { git = "https://git.coopcloud.tech/golgi-ssb/golgi.git" } log = "0.4" rocket = "0.5.0-rc.1" rocket_dyn_templates = { version = "0.1.0-rc.1", features = ["tera"] } serde = "1" serde_json = "1" sled = "0.34" +xdg = "2.4.1" diff --git a/src/db.rs b/src/db.rs index be95561..bc0ce0f 100644 --- a/src/db.rs +++ b/src/db.rs @@ -15,22 +15,65 @@ use log::{debug, info}; use serde::{Deserialize, Serialize}; use sled::{Batch, Db, IVec, Result, Tree}; -/// The latest sequence number of a Scuttlebutt peer. +/// Scuttlebutt peer data. #[derive(Debug, Deserialize, Serialize)] pub struct Peer { + /// The public key of the peer, also known as an SSB ID. + pub public_key: String, + /// The sequence number of the most recent post stored for this peer. pub latest_sequence: u64, - //pub name: String, - //pub posts: u16, + /// The name of the peer. + pub name: String, +} + +impl Peer { + /// Create a new instance of the Peer struct using the given public + /// key. Default values are set for latest_sequence and name. + pub fn new(public_key: &str) -> Peer { + Peer { + public_key: public_key.to_string(), + latest_sequence: 0, + name: "".to_string(), + } + } + + /// Modify the name field of an instance of the Peer struct, leaving + /// the other values unchanged. + pub fn set_name(self, name: &str) -> Peer { + Self { + name: name.to_string(), + ..self + } + } + + /// Modify the latest_sequence field of an instance of the Peer struct, + /// leaving the other values unchanged. + pub fn set_latest_sequence(self, latest_sequence: u64) -> Peer { + Self { + latest_sequence, + ..self + } + } } /// The text and metadata of a Scuttlebutt root post. #[derive(Debug, Deserialize, Serialize)] pub struct Post { + /// The key of the post-type message, also known as a message reference. pub key: String, + /// The text of the post (may be formatted as markdown). pub text: String, + /// The date the post was published (e.g. 17 May 2021). pub date: String, + /// The sequence number of the post-type message. pub sequence: u64, + /// The read state of the post; true if read, false if unread. pub read: bool, + /// The timestamp representing the date the post was published. + pub timestamp: i64, + /// The subject of the post, represented as the first 53 characters of + /// the post text. + pub subject: Option, } /// Convenience type which facilitates converting an IVec into a String. @@ -57,13 +100,16 @@ impl From for IVecString { } /// An instance of the key-value database and relevant trees. +#[allow(dead_code)] #[derive(Clone)] pub struct Database { - /// Stores the sled database instance. + /// The sled database instance. db: Db, - /// Stores the public keys of all the peers we are subscribed to. + /// A database tree containing Peer struct instances for all the peers + /// we are subscribed to. peer_tree: Tree, - /// Stores the posts (content and metadata) for all the feeds we are subscribed to. + /// A database tree containing Post struct instances for all of the posts + /// we have downloaded from the peer to whom we subscribe. pub post_tree: Tree, } @@ -74,16 +120,16 @@ impl Database { // Open the database at the given path. // The database will be created if it does not yet exist. // This code will panic if an IO error is encountered. - info!("initialising the sled database"); - let db = sled::open(path).expect("failed to open database"); - debug!("opening the 'peers' database tree"); + info!("Initialising sled database"); + let db = sled::open(path).expect("Failed to open database"); + debug!("Opening 'peers' database tree"); let peer_tree = db .open_tree("peers") - .expect("failed to open database peers tree"); - debug!("opening the 'posts' database tree"); + .expect("Failed to open database peers tree"); + debug!("Opening 'posts' database tree"); let post_tree = db .open_tree("posts") - .expect("failed to open database posts tree"); + .expect("Failed to open database posts tree"); Database { db, @@ -94,8 +140,10 @@ impl Database { /// Add a peer to the database by inserting the public key into the peer /// tree. - pub fn add_peer(&self, public_key: &str) -> Result> { - self.peer_tree.insert(&public_key, vec![0]) + pub fn add_peer(&self, peer: Peer) -> Result> { + let peer_bytes = bincode::serialize(&peer).unwrap(); + + self.peer_tree.insert(&peer.public_key, peer_bytes) } /// Remove a peer from the database, as represented by the given public @@ -104,15 +152,31 @@ impl Database { self.peer_tree.remove(&public_key).map(|_| ()) } - /// Get a list of all peers in the peer tree. The public key of each peer - /// is returned. - pub fn get_peers(&self) -> Vec { + /// Get a single peer from the peer tree, defined by the given public key. + /// The byte value for the matching entry, if found, is deserialized from + /// bincode into an instance of the Peer struct. + pub fn get_peer(&self, public_key: &str) -> Result> { + let peer = self + .peer_tree + .get(public_key.as_bytes()) + .unwrap() + .map(|peer| bincode::deserialize(&peer).unwrap()); + + Ok(peer) + } + + /// Get a list of all peers in the peer tree. The byte value for each + /// peer entry is deserialized from bincode into an instance of the Peer + /// struct. + pub fn get_peers(&self) -> Vec { + let mut peers = Vec::new(); + self.peer_tree .iter() - .keys() - .map(|bytes| IVecString::from(bytes.unwrap())) - .map(|ivec_string| ivec_string.string) - .collect() + .map(|peer| peer.unwrap()) + .for_each(|peer| peers.push(bincode::deserialize(&peer.1).unwrap())); + + peers } /// Add a post to the database by inserting an instance of the Post struct @@ -148,8 +212,9 @@ impl Database { } /// Get a list of all posts in the post tree authored by the given public - /// key. The byte value for each matching entry is deserialized from - /// bincode into an instance of the Post struct. + /// key and sort them by timestamp in descending order. The byte value for + /// each matching entry is deserialized from bincode into an instance of + /// the Post struct. pub fn get_posts(&self, public_key: &str) -> Result> { let mut posts = Vec::new(); @@ -158,6 +223,8 @@ impl Database { .map(|post| post.unwrap()) .for_each(|post| posts.push(bincode::deserialize(&post.1).unwrap())); + posts.sort_by(|a: &Post, b: &Post| b.timestamp.cmp(&a.timestamp)); + Ok(posts) } @@ -188,4 +255,22 @@ impl Database { // map the Option to (). self.post_tree.remove(post_key.as_bytes()).map(|_| ()) } + + /// Sum the total number of unread posts for the peer represented by the + /// given public key. + pub fn get_unread_post_count(&self, public_key: &str) -> u16 { + let mut unread_post_counter = 0; + + self.post_tree + .scan_prefix(public_key.as_bytes()) + .map(|post| post.unwrap()) + .for_each(|post| { + let deserialized_post: Post = bincode::deserialize(&post.1).unwrap(); + if !deserialized_post.read { + unread_post_counter += 1 + } + }); + + unread_post_counter + } } diff --git a/src/main.rs b/src/main.rs index 00b03d4..d044fde 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ mod sbot; mod task_loop; mod utils; -use std::{env, path::Path}; +use std::env; use async_std::channel; use log::info; @@ -14,6 +14,7 @@ use rocket::{ launch, routes, }; use rocket_dyn_templates::Template; +use xdg::BaseDirectories; use crate::{db::Database, routes::*, task_loop::Task}; @@ -25,18 +26,30 @@ pub struct WhoAmI { async fn rocket() -> _ { env_logger::init(); - let public_key: String = sbot::whoami().await.expect("whoami sbot call failed"); + // Retrieve and store the public key of the local sbot. + let public_key: String = sbot::whoami() + .await + .expect("whoami rpc call failed. please ensure the sbot is running before trying again"); + info!("Public key of the local sbot instance: {}", public_key); let whoami = WhoAmI { public_key }; - let db = Database::init(Path::new("lykin_db")); + // Create the key-value database. + let xdg_dirs = BaseDirectories::with_prefix("lykin").unwrap(); + let db_path = xdg_dirs + .place_config_file("database") + .expect("cannot create database directory"); + let db = Database::init(&db_path); let db_clone = db.clone(); + // Create a message passing channel. let (tx, rx) = channel::unbounded(); let tx_clone = tx.clone(); - task_loop::spawn(rx, db_clone).await; + // Spawn the task loop. + info!("Spawning task loop"); + task_loop::spawn(db_clone, rx).await; - info!("launching the web server"); + info!("Launching web server"); rocket::build() .manage(db) .manage(whoami) @@ -45,19 +58,21 @@ async fn rocket() -> _ { "/", routes![ home, + delete_post, + download_latest_posts, + mark_post_read, + mark_post_unread, + post, + posts, subscribe_form, unsubscribe_form, - posts, - post, - mark_post_read, - mark_post_unread ], ) .mount("/", FileServer::from(relative!("static"))) .attach(Template::fairing()) .attach(AdHoc::on_shutdown("cancel task loop", |_| { Box::pin(async move { - tx_clone.send(Task::Cancel).await; + tx_clone.send(Task::Cancel).await.unwrap(); }) })) } diff --git a/src/routes.rs b/src/routes.rs index 24a7675..4030b79 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -1,14 +1,17 @@ use async_std::channel::Sender; use log::{debug, warn}; -use markdown; use rocket::{form::Form, get, post, response::Redirect, uri, FromForm, State}; use rocket_dyn_templates::{tera::Context, Template}; -use uri_encode; -use crate::{db::Database, sbot, task_loop::Task, utils, WhoAmI}; +use crate::{ + db::{Database, Peer}, + sbot, + task_loop::Task, + utils, WhoAmI, +}; #[derive(FromForm)] -pub struct Peer { +pub struct PeerForm { pub public_key: String, } @@ -17,19 +20,44 @@ pub async fn home(db: &State) -> Template { let peers = db.get_peers(); let mut context = Context::new(); - context.insert("peers", &peers); + let mut peers_unread = Vec::new(); + + for peer in peers { + let unread_count = db.get_unread_post_count(&peer.public_key); + peers_unread.push((peer, unread_count.to_string())); + } + + context.insert("peers", &peers_unread); Template::render("base", &context.into_json()) } +#[get("/posts///delete")] +pub async fn delete_post(db: &State, public_key: &str, msg_id: &str) -> Redirect { + // Delete the post from the database. This method cannot panic, so we're + // safe to unwrap the result. + db.remove_post(public_key, msg_id).unwrap(); + + Redirect::to(uri!(posts(public_key))) +} + #[get("/posts/")] pub async fn posts(db: &State, public_key: &str) -> Template { let peers = db.get_peers(); - let posts = db.get_posts(public_key).unwrap(); let mut context = Context::new(); + let mut peers_unread = Vec::new(); + + for peer in peers { + let unread_count = db.get_unread_post_count(&peer.public_key); + peers_unread.push((peer, unread_count.to_string())); + } + + context.insert("peers", &peers_unread); + + let posts = db.get_posts(public_key).unwrap(); + context.insert("selected_peer", &public_key); - context.insert("peers", &peers); context.insert("posts", &posts); Template::render("base", &context.into_json()) @@ -38,10 +66,20 @@ pub async fn posts(db: &State, public_key: &str) -> Template { #[get("/posts//")] pub async fn post(db: &State, public_key: &str, msg_id: &str) -> Template { let peers = db.get_peers(); + + let mut context = Context::new(); + let mut peers_unread = Vec::new(); + + for peer in peers { + let unread_count = db.get_unread_post_count(&peer.public_key); + peers_unread.push((peer, unread_count.to_string())); + } + + context.insert("peers", &peers_unread); + let posts = db.get_posts(public_key).unwrap(); let post = db.get_post(public_key, msg_id).unwrap(); - let mut context = Context::new(); context.insert("selected_peer", &public_key); context.insert( "selected_peer_encoded", @@ -52,9 +90,7 @@ pub async fn post(db: &State, public_key: &str, msg_id: &str) -> Templ "selected_post_encoded", &uri_encode::encode_uri_component(msg_id), ); - context.insert("peers", &peers); context.insert("posts", &posts); - // TODO: consider converting markdown to html here context.insert("post", &post); context.insert("post_is_selected", &true); @@ -93,44 +129,76 @@ pub async fn mark_post_unread(db: &State, public_key: &str, msg_id: &s Redirect::to(uri!(post(public_key, msg_id))) } +#[get("/posts/download_latest")] +pub async fn download_latest_posts(db: &State, tx: &State>) -> Redirect { + for peer in db.get_peers() { + // Fetch the latest root posts authored by each peer we're + // subscribed to. Posts will be added to the key-value database. + if let Err(e) = tx + .send(Task::FetchLatestPosts(peer.public_key.clone())) + .await + { + warn!("task loop error: {}", e) + } + + // Fetch the latest name for each peer we're subscribed to and update + // the database. + if let Err(e) = tx.send(Task::FetchLatestName(peer.public_key)).await { + warn!("task loop error: {}", e) + } + } + + Redirect::to(uri!(home)) +} + #[post("/subscribe", data = "")] pub async fn subscribe_form( db: &State, whoami: &State, tx: &State>, - peer: Form, + peer: Form, ) -> Redirect { if utils::validate_public_key(&peer.public_key).is_ok() { debug!("public key {} is valid", &peer.public_key); - // TODO: consider getting the peer name here so we can insert it - match db.add_peer(&peer.public_key) { - Ok(_) => { - debug!("added {} to peer tree in database", &peer.public_key); - // TODO: i don't think we actually want to follow... - // we might still have the data in our ssb db, even if we don't follow - match sbot::is_following(&whoami.public_key, &peer.public_key).await { - Ok(status) if status.as_str() == "false" => { - match sbot::follow_peer(&peer.public_key).await { - Ok(_) => debug!("followed {}", &peer.public_key), - Err(e) => warn!("failed to follow {}: {}", &peer.public_key, e), - } - } - Ok(status) if status.as_str() == "true" => { - debug!("we already follow {}", &peer.public_key) - } - _ => (), - } - let peer = peer.public_key.to_string(); - // Fetch all root posts authored by the peer we're subscribing - // to. Posts will be added to the key-value database. - if let Err(e) = tx.send(Task::FetchAll(peer)).await { - warn!("task loop error: {}", e) - } + + // Retrieve the name of the peer to which we are subscribing. + let peer_name = match sbot::get_name(&peer.public_key).await { + Ok(name) => name, + Err(e) => { + warn!("failed to fetch name for {}: {}", &peer.public_key, e); + String::from("") } - Err(_e) => warn!( + }; + let peer_info = Peer::new(&peer.public_key).set_name(&peer_name); + + // Add the peer to the database and then check the follow state. + // Follow the peer if our local instance is not already following. + if db.add_peer(peer_info).is_ok() { + debug!("added {} to peer tree in database", &peer.public_key); + match sbot::is_following(&whoami.public_key, &peer.public_key).await { + Ok(status) if status.as_str() == "false" => { + match sbot::follow_peer(&peer.public_key).await { + Ok(_) => debug!("followed {}", &peer.public_key), + Err(e) => warn!("failed to follow {}: {}", &peer.public_key, e), + } + } + Ok(status) if status.as_str() == "true" => { + debug!("we already follow {}", &peer.public_key) + } + _ => (), + } + + let peer_id = peer.public_key.to_string(); + // Fetch all root posts authored by the peer we're subscribing + // to. Posts will be added to the key-value database. + if let Err(e) = tx.send(Task::FetchAllPosts(peer_id)).await { + warn!("task loop error: {}", e) + } + } else { + warn!( "failed to add {} to peer tree in database", &peer.public_key - ), + ) } } else { warn!("{} is invalid", &peer.public_key); @@ -140,20 +208,18 @@ pub async fn subscribe_form( } #[post("/unsubscribe", data = "")] -pub fn unsubscribe_form(db: &State, peer: Form) -> Redirect { - // validate the public key - match utils::validate_public_key(&peer.public_key) { - Ok(_) => { - debug!("public key {} is valid", &peer.public_key); - match db.remove_peer(&peer.public_key) { - Ok(_) => debug!("removed {} from peer tree in database", &peer.public_key), - Err(_e) => warn!( - "failed to remove {} from peer tree in database", - &peer.public_key - ), - } +pub fn unsubscribe_form(db: &State, peer: Form) -> Redirect { + if let Err(e) = utils::validate_public_key(&peer.public_key) { + warn!("{} is invalid: {}", &peer.public_key, e) + } else { + debug!("public key {} is valid", &peer.public_key); + match db.remove_peer(&peer.public_key) { + Ok(_) => debug!("removed {} from peer tree in database", &peer.public_key), + Err(_e) => warn!( + "failed to remove {} from peer tree in database", + &peer.public_key + ), } - Err(e) => warn!("{} is invalid: {}", &peer.public_key, e), } Redirect::to(uri!(home)) diff --git a/src/sbot.rs b/src/sbot.rs index 7d0dda9..06c3d78 100644 --- a/src/sbot.rs +++ b/src/sbot.rs @@ -3,30 +3,39 @@ use async_std::stream::StreamExt; use chrono::NaiveDateTime; use golgi::{ - api::friends::RelationshipQuery, + api::{friends::RelationshipQuery, history_stream::CreateHistoryStream}, messages::{SsbMessageContentType, SsbMessageKVT}, sbot::Keystore, GolgiError, Sbot, }; -use log::warn; +use log::{debug, info, warn}; use serde_json::value::Value; use crate::db::Post; +/// Initialise a connection to a Scuttlebutt server. +async fn init_sbot() -> Result { + let keystore = Keystore::GoSbot; + let ip_port = Some("127.0.0.1:8021".to_string()); + let net_id = None; + + debug!("Initialising the sbot connection"); + Sbot::init(keystore, ip_port, net_id) + .await + .map_err(|e| e.to_string()) +} + /// Return the public key of the local sbot instance. pub async fn whoami() -> Result { - let mut sbot = Sbot::init(Keystore::Patchwork, None, None) - .await - .map_err(|e| e.to_string())?; + let mut sbot = init_sbot().await?; + info!("Executing `whoami` call"); sbot.whoami().await.map_err(|e| e.to_string()) } /// Follow a peer. pub async fn follow_peer(public_key: &str) -> Result { - let mut sbot = Sbot::init(Keystore::Patchwork, None, None) - .await - .map_err(|e| e.to_string())?; + let mut sbot = init_sbot().await?; sbot.follow(public_key).await.map_err(|e| e.to_string()) } @@ -35,9 +44,7 @@ pub async fn follow_peer(public_key: &str) -> Result { /// /// Is peer A (`public_key_a`) following peer B (`public_key_b`)? pub async fn is_following(public_key_a: &str, public_key_b: &str) -> Result { - let mut sbot = Sbot::init(Keystore::Patchwork, None, None) - .await - .map_err(|e| e.to_string())?; + let mut sbot = init_sbot().await?; let query = RelationshipQuery { source: public_key_a.to_string(), @@ -54,17 +61,29 @@ pub async fn is_following(public_key_a: &str, public_key_b: &str) -> Result impl futures::Stream> { - let mut sbot = Sbot::init(Keystore::Patchwork, None, None) - .await - .map_err(|e| e.to_string()) - .unwrap(); + let mut sbot = init_sbot().await.unwrap(); - sbot.create_history_stream(public_key.to_string()) + let history_stream_args = CreateHistoryStream::new(public_key.to_string()) + .keys_values(true, true) + .after_seq(sequence_number); + + sbot.create_history_stream(history_stream_args) .await .unwrap() } +/// Return the name (self-identifier) for the peer associated with the given +/// public key. +/// +/// The public key of the peer will be returned if a name is not found. +pub async fn get_name(public_key: &str) -> Result { + let mut sbot = init_sbot().await?; + + sbot.get_name(public_key).await.map_err(|e| e.to_string()) +} + /// Filter a stream of messages and return a vector of root posts. /// /// Each returned vector element includes the key of the post, the content @@ -72,7 +91,8 @@ pub async fn get_message_stream( /// and whether it is read or unread. pub async fn get_root_posts( history_stream: impl futures::Stream>, -) -> Vec { +) -> (u64, Vec) { + let mut latest_sequence = 0; let mut posts = Vec::new(); futures::pin_mut!(history_stream); @@ -84,18 +104,23 @@ pub async fn get_root_posts( let content = msg.value.content.to_owned(); if let Value::Object(content_map) = content { if !content_map.contains_key("root") { - let timestamp_int = msg.value.timestamp.round() as i64 / 1000; - let datetime = NaiveDateTime::from_timestamp(timestamp_int, 0); + let timestamp = msg.value.timestamp.round() as i64 / 1000; + let datetime = NaiveDateTime::from_timestamp(timestamp, 0); let date = datetime.format("%d %b %Y").to_string(); - let text = content_map.get_key_value("text").unwrap(); + let text = content_map.get_key_value("text").unwrap().1.to_string(); + let subject = text.get(0..52).map(|s| s.to_string()); + + latest_sequence = msg.value.sequence; posts.push(Post { key: msg.key.to_owned(), - text: text.1.to_string(), + text, + timestamp, date, sequence: msg.value.sequence, read: false, + subject, }) } } @@ -108,5 +133,5 @@ pub async fn get_root_posts( } } - posts + (latest_sequence, posts) } diff --git a/src/task_loop.rs b/src/task_loop.rs index 5ddf9b6..b2ea4e4 100644 --- a/src/task_loop.rs +++ b/src/task_loop.rs @@ -1,42 +1,89 @@ use async_std::{channel::Receiver, task}; -use log::{debug, info, warn}; +use log::{info, warn}; use crate::{db::Database, sbot}; pub enum Task { Cancel, - FetchAll(String), - //FetchLatest(String), + FetchAllPosts(String), + FetchLatestPosts(String), + FetchLatestName(String), } -pub async fn spawn(rx: Receiver, db: Database) { +async fn fetch_posts_and_update_db(db: &Database, peer_id: String, after_sequence: u64) { + let peer_msgs = sbot::get_message_stream(&peer_id, after_sequence).await; + let (latest_sequence, root_posts) = sbot::get_root_posts(peer_msgs).await; + + match db.add_post_batch(&peer_id, root_posts) { + Ok(_) => { + info!( + "Inserted batch of posts into database post tree for peer: {}", + &peer_id + ) + } + Err(e) => warn!( + "Failed to insert batch of posts into database post tree for peer: {}: {}", + &peer_id, e + ), + } + + // Update the value of the latest sequence number for + // the peer (this is stored in the database). + if let Ok(Some(peer)) = db.get_peer(&peer_id) { + db.add_peer(peer.set_latest_sequence(latest_sequence)) + .unwrap(); + } +} + +async fn fetch_name_and_update_db(db: &Database, peer_id: String) { + match sbot::get_name(&peer_id).await { + Ok(name) => { + if let Ok(Some(peer)) = db.get_peer(&peer_id) { + let updated_peer = peer.set_name(&name); + match db.add_peer(updated_peer) { + Ok(_) => info!("Updated name for peer: {}", &peer_id), + Err(e) => { + warn!("Failed to update name for peer: {}: {}", &peer_id, e) + } + } + } + } + Err(e) => warn!("Failed to fetch name for {}: {}", &peer_id, e), + } +} + +pub async fn spawn(db: Database, rx: Receiver) { task::spawn(async move { while let Ok(task) = rx.recv().await { match task { // Fetch all messages authored by the given peer, filter - // the root posts and insert them into the peer tree of the + // the root posts and insert them into the posts tree of the // database. - Task::FetchAll(peer) => { - let peer_msgs = sbot::get_message_stream(&peer).await; - // TODO: return a tuple from sbot::get_root_posts - //let (root_posts, latest_sequence) = sbot::get_root_posts(peer_msgs).await; - // TODO: update the sequence number if required - //if latest_sequence > db.get_latest_sequence(&peer) { - // db.update_sequence(&peer, latest_sequence) - //} - let root_posts = sbot::get_root_posts(peer_msgs).await; - match db.add_post_batch(&peer, root_posts) { - Ok(_) => debug!("inserted batch of posts into post tree for {}", &peer), - Err(e) => warn!( - "failed to insert batch of posts into post tree for {}: {}", - &peer, e - ), + Task::FetchAllPosts(peer_id) => { + info!("Fetching all posts for peer: {}", peer_id); + fetch_posts_and_update_db(&db, peer_id, 0).await; + } + // Fetch only the latest messages authored by the given peer, + // ie. messages with sequence numbers greater than those + // which are already stored in the database. + // + // Retrieve the root posts from those messages and insert them + // into the posts tree of the database. + Task::FetchLatestPosts(peer_id) => { + if let Ok(Some(peer)) = db.get_peer(&peer_id) { + info!("Fetching latest posts for peer: {}", peer_id); + fetch_posts_and_update_db(&db, peer_id, peer.latest_sequence).await; } } - // TODO: fetch all msgs with sequence number > peer.latest_sequence for peer - //Task::FetchLatest(peer) => { + // Fetch the latest name for the given peer and update the + // peer entry in the peers tree of the database. + Task::FetchLatestName(peer_id) => { + info!("Fetching latest name for peer: {}", peer_id); + fetch_name_and_update_db(&db, peer_id).await; + } + // Break out of the task loop. Task::Cancel => { - info!("exiting task loop..."); + info!("Exiting task loop..."); break; } } diff --git a/src/utils.rs b/src/utils.rs index f891dd9..8ab2fe8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -2,26 +2,26 @@ /// /// Return an error string if the key is invalid. pub fn validate_public_key(public_key: &str) -> Result<(), String> { - // ensure the id starts with the correct sigil link + // Ensure the ID starts with the correct sigil link. if !public_key.starts_with('@') { return Err("expected '@' sigil as first character".to_string()); } - // find the dot index denoting the start of the algorithm definition tag + // Find the dot index denoting the start of the algorithm definition tag. let dot_index = match public_key.rfind('.') { Some(index) => index, None => return Err("no dot index was found".to_string()), }; - // check hashing algorithm (must end with ".ed25519") + // Check the hashing algorithm (must end with ".ed25519"). if !&public_key.ends_with(".ed25519") { return Err("hashing algorithm must be ed25519".to_string()); } - // obtain the base64 portion (substring) of the public key + // Obtain the base64 portion (substring) of the public key. let base64_str = &public_key[1..dot_index]; - // length of a base64 encoded ed25519 public key + // Ensure the length of the base64 encoded ed25519 public key is correct. if base64_str.len() != 44 { return Err("base64 data length is incorrect".to_string()); } diff --git a/static/css/lykin.css b/static/css/lykin.css index d52779c..a5433d7 100644 --- a/static/css/lykin.css +++ b/static/css/lykin.css @@ -1,34 +1,6 @@ -.nav { - background-color: lightgreen; - border: 5px solid #19A974; - border-radius: 15px; - grid-area: nav; - padding: 1rem; -} - -.peers { - background-color: lightblue; - border: 5px solid #357EDD; - border-radius: 15px; - grid-area: peers; -} - -.posts { - background-color: bisque; - border: 5px solid #FF6300; - border-radius: 15px; - grid-area: posts; - overflow-y: scroll; -} - -.post > ul { - padding-left: 25px; - padding-right: 25px; -} - .content { background-color: lightyellow; - border: 5px solid #FFD700; + border: 5px solid #ffd700; border-radius: 15px; grid-area: content; padding: 1.5rem; @@ -42,34 +14,9 @@ margin: 0; } -/* -.flex-container { - display: flex; - justify-content: space-between; -} -*/ - -.grid-container { - display: grid; - grid-template-columns: repeat(4, 1fr); - grid-template-rows: 1fr 3fr 4fr; - grid-template-areas: - 'nav nav nav nav nav' - 'peers posts posts posts posts' - 'peers content content content content'; - grid-gap: 10px; - padding-left: 15px; - padding-right: 15px; - padding-top: 5px; - overflow: hidden; - height: 85vh; -} - -.grid-container > div { - /* background-color: rgba(255, 255, 255, 0.8); */ - /* text-align: center; */ - /* padding: 20px 0; */ - /* font-size: 30px; */ +.disabled { + opacity: 0.4; + pointer-events: none; } .flex-container { @@ -82,4 +29,119 @@ margin: 5px; } -a { text-decoration: none; color: black; } +.grid-container { + display: grid; + grid-template-columns: 1fr; + grid-template-rows: 1fr 1fr 1fr 3fr; + grid-template-areas: + 'nav' + 'peers' + 'posts' + 'content'; + grid-gap: 10px; + padding-left: 15px; + padding-right: 15px; + padding-top: 5px; + overflow: hidden; + height: 85vh; +} + +@media only screen and (min-width: 600px) { + .grid-container { + grid-template-columns: repeat(4, 1fr); + grid-template-rows: 1fr 3fr 4fr; + grid-template-areas: + 'nav nav nav nav nav' + 'peers posts posts posts posts' + 'peers content content content content'; + } +} + +.icon { + margin-left: 20px; +} + +.nav { + background-color: lightgreen; + border: 5px solid #19a974; + border-radius: 15px; + grid-area: nav; + padding: 1rem; +} + +.peers { + background-color: lightblue; + border: 5px solid #357edd; + border-radius: 15px; + grid-area: peers; + text-align: left; +} + +.peers > ul { + padding-left: 1rem; +} + +.peers > ul > li > a { + justify-content: space-between; +} + +.peers > ul > li > a > p { + margin: 0; +} + +.post > ul { + padding-left: 25px; + padding-right: 25px; +} + +.posts { + background-color: bisque; + border: 5px solid #ff6300; + border-radius: 15px; + grid-area: posts; + overflow-y: scroll; +} + +.posts > ul { + padding-left: 1rem; + padding-right: 1rem; +} + +.posts > ul > li > a { + justify-content: space-between; +} + +.posts > ul > li > a > p { + margin: 0; +} + +.selected { + background-color: #f9c587; +} + +a { + text-decoration: none; + color: black; +} + +code { + word-wrap: anywhere; +} + +form { + margin-left: auto; + margin-right: 10px; +} + +h1 { + margin-left: 15px; +} + +img { + width: 55px; +} + +li { + list-style: none; + font-size: 12px; +} diff --git a/templates/base.html.tera b/templates/base.html.tera index 629dbf4..2be5bb5 100644 --- a/templates/base.html.tera +++ b/templates/base.html.tera @@ -9,7 +9,9 @@ -

lykin

+ +

lykin

+
{% include "topbar" %} {% include "peer_list" %} diff --git a/templates/peer_list.html.tera b/templates/peer_list.html.tera index 1f9ee02..5d25f01 100644 --- a/templates/peer_list.html.tera +++ b/templates/peer_list.html.tera @@ -1,9 +1,18 @@ -
-