diff --git a/.env b/.env index 8332a0a..c60e236 100644 --- a/.env +++ b/.env @@ -1,2 +1 @@ -OUT_DIR="compiled_templates" DATABASE_URL=postgres://ap_actix:ap_actix@localhost:5432/ap_actix diff --git a/Cargo.lock b/Cargo.lock index 2adb230..6523cab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,13 +2,13 @@ # It is not intended for manual editing. [[package]] name = "activitystreams" -version = "0.7.0-alpha.8" +version = "0.7.0-alpha.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e9fedbe571e267d9b93d071bdc4493f944022c6cce717ebb27d352026fc81c4" +checksum = "fe7ceed015dfca322d3bcec3653909c77557e7e57df72e98cb8806e2c93cc919" dependencies = [ "chrono", "mime", - "serde 1.0.118", + "serde 1.0.123", "serde_json", "thiserror", "url", @@ -21,7 +21,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb8e19a0810cc25df3535061a08b7d8f8a734d309ea4411c57a9767e4a2ffa0e" dependencies = [ "activitystreams", - "serde 1.0.118", + "serde 1.0.123", "serde_json", ] @@ -32,7 +32,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78d1833b3838dbe990df0f1f87baf640cf6146e898166afe401839d1b001e570" dependencies = [ "bitflags", - "bytes", + "bytes 0.5.6", "futures-core", "futures-sink", "log", @@ -79,7 +79,7 @@ dependencies = [ "base64 0.13.0", "bitflags", "brotli2", - "bytes", + "bytes 0.5.6", "cookie", "copyless", "derive_more", @@ -100,15 +100,15 @@ dependencies = [ "log", "mime", "percent-encoding", - "pin-project 1.0.2", + "pin-project 1.0.5", "rand", "regex", - "serde 1.0.118", + "serde 1.0.123", "serde_json", "serde_urlencoded", "sha-1", "slab", - "time 0.2.23", + "time 0.2.25", ] [[package]] @@ -123,15 +123,15 @@ dependencies = [ [[package]] name = "actix-router" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd1f7dbda1645bf7da33554db60891755f6c01c1b2169e2f4c492098d30c235" +checksum = "2ad299af73649e1fc893e333ccf86f377751eb95ff875d095131574c6f43452c" dependencies = [ "bytestring", "http", "log", "regex", - "serde 1.0.118", + "serde 1.0.123", ] [[package]] @@ -234,7 +234,7 @@ dependencies = [ "actix-rt", "actix-service", "bitflags", - "bytes", + "bytes 0.5.6", "either", "futures-channel", "futures-sink", @@ -263,7 +263,7 @@ dependencies = [ "actix-utils", "actix-web-codegen", "awc", - "bytes", + "bytes 0.5.6", "derive_more", "encoding_rs", "futures-channel", @@ -272,14 +272,14 @@ dependencies = [ "fxhash", "log", "mime", - "pin-project 1.0.2", + "pin-project 1.0.5", "regex", "rustls", - "serde 1.0.118", + "serde 1.0.123", "serde_json", "serde_urlencoded", "socket2", - "time 0.2.23", + "time 0.2.25", "tinyvec", "url", ] @@ -302,16 +302,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04d2d2618ebae185c8178e09921854e18ecb0824f9892085107d6a254a4605e5" dependencies = [ "actix-web", - "serde 1.0.118", + "serde 1.0.123", "serde_derive", "thiserror", ] [[package]] name = "addr2line" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423" +checksum = "a55f82cfe485775d02112886f4169bde0c5894d75e79ead7eafe7e40a25e45f7" dependencies = [ "gimli", ] @@ -363,9 +363,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" [[package]] name = "arrayvec" @@ -437,7 +437,7 @@ dependencies = [ "actix-rt", "actix-service", "base64 0.13.0", - "bytes", + "bytes 0.5.6", "cfg-if 1.0.0", "derive_more", "futures-core", @@ -446,7 +446,7 @@ dependencies = [ "percent-encoding", "rand", "rustls", - "serde 1.0.118", + "serde 1.0.123", "serde_json", "serde_urlencoded", ] @@ -463,9 +463,9 @@ dependencies = [ [[package]] name = "background-jobs-actix" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9696729d7b14c1682af423381744f317f7180136b4a2c8e4eca3a46e242f5fc" +checksum = "5a0caa1f592d2bcfde1e3b38db933215320e2402e75dc60afbfedbf59b831a74" dependencies = [ "actix-rt", "anyhow", @@ -476,7 +476,7 @@ dependencies = [ "log", "num_cpus", "rand", - "serde 1.0.118", + "serde 1.0.123", "serde_json", "thiserror", "tokio", @@ -495,7 +495,7 @@ dependencies = [ "async-trait", "chrono", "log", - "serde 1.0.118", + "serde 1.0.123", "serde_json", "thiserror", "tokio", @@ -504,9 +504,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.55" +version = "0.3.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598" +checksum = "9d117600f438b1707d4e4ae15d3595657288f8235a0eb593e80ecc98ab34e1bc" dependencies = [ "addr2line", "cfg-if 1.0.0", @@ -577,9 +577,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.4.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9" [[package]] name = "bytecount" @@ -589,9 +589,9 @@ checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" [[package]] name = "byteorder" -version = "1.3.4" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" [[package]] name = "bytes" @@ -600,12 +600,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" [[package]] -name = "bytestring" -version = "0.1.5" +name = "bytes" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7c05fa5172da78a62d9949d662d2ac89d4cc7355d7b49adee5163f1fb3f363" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "bytestring" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" dependencies = [ - "bytes", + "bytes 1.0.1", ] [[package]] @@ -635,7 +641,7 @@ dependencies = [ "libc", "num-integer", "num-traits 0.2.14", - "serde 1.0.118", + "serde 1.0.123", "time 0.1.44", "winapi 0.3.9", ] @@ -664,7 +670,7 @@ dependencies = [ "lazy_static", "nom", "rust-ini", - "serde 1.0.118", + "serde 1.0.123", "serde-hjson", "serde_json", "toml", @@ -673,9 +679,9 @@ dependencies = [ [[package]] name = "const_fn" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" +checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6" [[package]] name = "cookie" @@ -684,7 +690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f" dependencies = [ "percent-encoding", - "time 0.2.23", + "time 0.2.25", "version_check", ] @@ -710,67 +716,30 @@ dependencies = [ ] [[package]] -name = "crossbeam-queue" -version = "0.2.3" +name = "crossbeam-epoch" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" +checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", + "const_fn", "crossbeam-utils", - "maybe-uninit", + "lazy_static", + "memoffset", + "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.7.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d" dependencies = [ "autocfg 1.0.1", - "cfg-if 0.1.10", + "cfg-if 1.0.0", "lazy_static", ] -[[package]] -name = "crypto-mac" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bcd97a54c7ca5ce2f6eb16f6bede5b0ab5f0055fedc17d2f0b4466e21671ca" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "deadpool" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aaff9a7a1de9893f4004fa08527b31cb2ae4121c44e053cf53f29203c73bd23" -dependencies = [ - "async-trait", - "config", - "crossbeam-queue", - "num_cpus", - "serde 1.0.118", - "tokio", -] - -[[package]] -name = "deadpool-postgres" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faad41e7f93dd682108c72aec029e5bc6238e7df64c9d84832525d4033d2e726" -dependencies = [ - "async-trait", - "config", - "deadpool", - "futures", - "log", - "serde 1.0.118", - "tokio", - "tokio-postgres", -] - [[package]] name = "derive_more" version = "0.99.11" @@ -811,9 +780,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "encoding_rs" -version = "0.8.26" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283" +checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" dependencies = [ "cfg-if 1.0.0", ] @@ -850,7 +819,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e" dependencies = [ "atty", - "humantime 2.0.1", + "humantime 2.1.0", "log", "regex", "termcolor", @@ -862,17 +831,11 @@ version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - [[package]] name = "flate2" -version = "1.0.19" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129" +checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0" dependencies = [ "cfg-if 1.0.0", "crc32fast", @@ -896,6 +859,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -924,9 +897,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0" +checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150" dependencies = [ "futures-channel", "futures-core", @@ -939,9 +912,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64" +checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846" dependencies = [ "futures-core", "futures-sink", @@ -949,15 +922,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748" +checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65" [[package]] name = "futures-executor" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65" +checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9" dependencies = [ "futures-core", "futures-task", @@ -966,15 +939,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb" +checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500" [[package]] name = "futures-macro" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556" +checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd" dependencies = [ "proc-macro-hack", "proc-macro2", @@ -984,24 +957,24 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d" +checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6" [[package]] name = "futures-task" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d" +checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86" dependencies = [ "once_cell", ] [[package]] name = "futures-util" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2" +checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b" dependencies = [ "futures-channel", "futures-core", @@ -1010,7 +983,7 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project 1.0.2", + "pin-project-lite 0.2.4", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -1038,15 +1011,26 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] +[[package]] +name = "getrandom" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", +] + [[package]] name = "gimli" version = "0.23.0" @@ -1059,7 +1043,7 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" dependencies = [ - "bytes", + "bytes 0.5.6", "fnv", "futures-core", "futures-sink", @@ -1093,23 +1077,13 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ "libc", ] -[[package]] -name = "hmac" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff" -dependencies = [ - "crypto-mac", - "digest", -] - [[package]] name = "hostname" version = "0.3.1" @@ -1137,11 +1111,11 @@ dependencies = [ [[package]] name = "http" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84129d298a6d57d246960ff8eb831ca4af3f96d29e2e28848dae275408658e26" +checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" dependencies = [ - "bytes", + "bytes 1.0.1", "fnv", "itoa", ] @@ -1165,7 +1139,7 @@ dependencies = [ "actix-web", "awc", "base64 0.12.3", - "bytes", + "bytes 0.5.6", "chrono", "futures", "http-signature-normalization", @@ -1176,9 +1150,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.3.4" +version = "1.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" +checksum = "615caabe2c3160b313d52ccc905335f4ed5f10881dd63dc5699d47e90be85691" [[package]] name = "humantime" @@ -1191,15 +1165,15 @@ dependencies = [ [[package]] name = "humantime" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "idna" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +checksum = "de910d521f7cc3135c4de8db1cb910e0b5ed1dc6f57c381cd07e8e661ce10094" dependencies = [ "matches", "unicode-bidi", @@ -1263,9 +1237,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "js-sys" -version = "0.3.46" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175" +checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65" dependencies = [ "wasm-bindgen", ] @@ -1310,9 +1284,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.81" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" [[package]] name = "libm" @@ -1332,9 +1306,9 @@ dependencies = [ [[package]] name = "linked-hash-map" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "lock_api" @@ -1347,18 +1321,18 @@ dependencies = [ [[package]] name = "log" -version = "0.4.11" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", ] [[package]] name = "lru" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aae342b73d57ad0b8b364bd12584819f2c1fe9114285dfcf8b0722607671635" +checksum = "fe2382d8ed3918ea4ba70d98d5b74e47a168e0331965f3f17cdbc748bdebc5a3" dependencies = [ "hashbrown", ] @@ -1369,7 +1343,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" dependencies = [ - "linked-hash-map 0.5.3", + "linked-hash-map 0.5.4", ] [[package]] @@ -1393,7 +1367,7 @@ dependencies = [ "log", "phf", "phf_codegen", - "serde 1.0.118", + "serde 1.0.123", "serde_derive", "serde_json", "string_cache", @@ -1425,12 +1399,6 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "md5" version = "0.7.0" @@ -1443,6 +1411,15 @@ version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +[[package]] +name = "memoffset" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87" +dependencies = [ + "autocfg 1.0.1", +] + [[package]] name = "mime" version = "0.3.16" @@ -1576,7 +1553,7 @@ dependencies = [ "num-iter", "num-traits 0.2.14", "rand", - "serde 1.0.118", + "serde 1.0.123", "smallvec", "zeroize", ] @@ -1643,9 +1620,9 @@ dependencies = [ [[package]] name = "object" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397" +checksum = "a9a7ab5d64814df0fe4a4b5ead45ed6c5f181ee3ff04ba344313a6c80446c5d4" [[package]] name = "once_cell" @@ -1750,11 +1727,11 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7" +checksum = "96fa8ebb90271c4477f144354485b8068bd8f6b78b428b01ba892ca26caf0b63" dependencies = [ - "pin-project-internal 1.0.2", + "pin-project-internal 1.0.5", ] [[package]] @@ -1770,9 +1747,9 @@ dependencies = [ [[package]] name = "pin-project-internal" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f" +checksum = "758669ae3558c6f74bd2a18b41f7ac0b5a195aea6639d6a9b5e5d1ad5ba24c0b" dependencies = [ "proc-macro2", "quote", @@ -1787,9 +1764,9 @@ checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" [[package]] name = "pin-project-lite" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c" +checksum = "439697af366c49a6d0a010c56a0d97685bc140ce0d377b13a2ea2aa42d64a827" [[package]] name = "pin-utils" @@ -1797,39 +1774,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "postgres-protocol" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4888a0e36637ab38d76cace88c1476937d617ad015f07f6b669cec11beacc019" -dependencies = [ - "base64 0.13.0", - "byteorder", - "bytes", - "fallible-iterator", - "hmac", - "md5", - "memchr", - "rand", - "sha2", - "stringprep", -] - -[[package]] -name = "postgres-types" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc08a7d94a80665de4a83942fa8db2fdeaf2f123fc0535e384dc4fff251efae" -dependencies = [ - "bytes", - "chrono", - "fallible-iterator", - "postgres-protocol", - "serde 1.0.118", - "serde_json", - "uuid", -] - [[package]] name = "ppv-lite86" version = "0.2.10" @@ -1884,9 +1828,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" [[package]] name = "proc-macro-nested" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba180dafb9038b050a4c280019bbedf9f2467b61e5d892dcad585bb57aadc5a" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" @@ -1918,7 +1862,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom", + "getrandom 0.1.16", "libc", "rand_chacha", "rand_core", @@ -1942,7 +1886,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom", + "getrandom 0.1.16", ] [[package]] @@ -1971,9 +1915,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" [[package]] name = "regex" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" dependencies = [ "aho-corasick", "memchr", @@ -1983,13 +1927,13 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.21" +version = "0.6.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" [[package]] name = "relay" -version = "0.1.0" +version = "0.2.0" dependencies = [ "activitystreams", "activitystreams-ext", @@ -2005,8 +1949,6 @@ dependencies = [ "base64 0.13.0", "chrono", "config", - "deadpool", - "deadpool-postgres", "dotenv", "env_logger 0.8.2", "futures", @@ -2021,12 +1963,12 @@ dependencies = [ "rsa-magic-public-key", "rsa-pem", "ructe", - "serde 1.0.118", + "serde 1.0.123", "serde_json", "sha2", + "sled", "structopt", "thiserror", - "tokio-postgres", "ttl_cache", "uuid", ] @@ -2043,9 +1985,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.16.19" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ "cc", "libc", @@ -2217,9 +2159,9 @@ checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" [[package]] name = "serde" -version = "1.0.118" +version = "1.0.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800" +checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" dependencies = [ "serde_derive", ] @@ -2239,9 +2181,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.118" +version = "1.0.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df" +checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" dependencies = [ "proc-macro2", "quote", @@ -2250,13 +2192,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" +checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" dependencies = [ "itoa", "ryu", - "serde 1.0.118", + "serde 1.0.123", ] [[package]] @@ -2277,14 +2219,14 @@ dependencies = [ "form_urlencoded", "itoa", "ryu", - "serde 1.0.118", + "serde 1.0.123", ] [[package]] name = "sha-1" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c" +checksum = "f4b312c3731e3fe78a185e6b9b911a7aa715b8e31cce117975219aab2acf285d" dependencies = [ "block-buffer", "cfg-if 1.0.0", @@ -2301,9 +2243,9 @@ checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" [[package]] name = "sha2" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8" +checksum = "fa827a14b29ab7f44778d14a88d3cb76e949c45083f7dbfa507d0cb699dc12de" dependencies = [ "block-buffer", "cfg-if 1.0.0", @@ -2345,10 +2287,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" [[package]] -name = "smallvec" -version = "1.5.1" +name = "sled" +version = "0.34.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75" +checksum = "1d0132f3e393bcb7390c60bb45769498cf4550bcb7a21d7f95c02b69f6362cdc" +dependencies = [ + "crc32fast", + "crossbeam-epoch", + "crossbeam-utils", + "fs2", + "fxhash", + "libc", + "log", + "parking_lot", +] + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "socket2" @@ -2369,9 +2327,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] name = "standback" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf906c8b8fc3f6ecd1046e01da1d8ddec83e48c8b08b84dcc02b585a6bedf5a8" +checksum = "a2beb4d1860a61f571530b3f855a1b538d0200f7871c63331ecd6f17b1f014f8" dependencies = [ "version_check", ] @@ -2404,7 +2362,7 @@ checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" dependencies = [ "proc-macro2", "quote", - "serde 1.0.118", + "serde 1.0.123", "serde_derive", "syn", ] @@ -2418,7 +2376,7 @@ dependencies = [ "base-x", "proc-macro2", "quote", - "serde 1.0.118", + "serde 1.0.123", "serde_derive", "serde_json", "sha1", @@ -2441,7 +2399,7 @@ dependencies = [ "new_debug_unreachable", "phf_shared", "precomputed-hash", - "serde 1.0.118", + "serde 1.0.123", ] [[package]] @@ -2456,16 +2414,6 @@ dependencies = [ "quote", ] -[[package]] -name = "stringprep" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "strsim" version = "0.8.0" @@ -2504,9 +2452,9 @@ checksum = "1e81da0851ada1f3e9d4312c704aa4f8806f0f9d69faaf8df2f3464b4a9437c2" [[package]] name = "syn" -version = "1.0.56" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" dependencies = [ "proc-macro2", "quote", @@ -2527,9 +2475,9 @@ dependencies = [ [[package]] name = "tendril" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707feda9f2582d5d680d733e38755547a3e8fb471e7ba11452ecfd9ce93a5d3b" +checksum = "a9ef557cb397a4f0a5a3a628f06515f78563f2209e64d47055d9dc6052bf5e33" dependencies = [ "futf", "mac", @@ -2576,11 +2524,11 @@ dependencies = [ [[package]] name = "thread_local" -version = "1.0.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] @@ -2605,9 +2553,9 @@ dependencies = [ [[package]] name = "time" -version = "0.2.23" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcdaeea317915d59b2b4cd3b5efcd156c309108664277793f5351700c02ce98b" +checksum = "1195b046942c221454c2539395f85413b33383a067449d78aab2b7b052a142f7" dependencies = [ "const_fn", "libc", @@ -2643,9 +2591,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" dependencies = [ "tinyvec_macros", ] @@ -2658,11 +2606,11 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "0.2.24" +version = "0.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48" +checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" dependencies = [ - "bytes", + "bytes 0.5.6", "fnv", "futures-core", "iovec", @@ -2677,28 +2625,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "tokio-postgres" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a2482c9fe4dd481723cf5c0616f34afc710e55dcda0944e12e7b3316117892" -dependencies = [ - "async-trait", - "byteorder", - "bytes", - "fallible-iterator", - "futures", - "log", - "parking_lot", - "percent-encoding", - "phf", - "pin-project-lite 0.1.11", - "postgres-protocol", - "postgres-types", - "tokio", - "tokio-util", -] - [[package]] name = "tokio-rustls" version = "0.14.1" @@ -2717,7 +2643,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" dependencies = [ - "bytes", + "bytes 0.5.6", "futures-core", "futures-sink", "log", @@ -2731,18 +2657,18 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" dependencies = [ - "serde 1.0.118", + "serde 1.0.123", ] [[package]] name = "tracing" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3" +checksum = "f7d40a22fd029e33300d8d89a5cc8ffce18bb7c587662f54629e94c9de5487f3" dependencies = [ "cfg-if 1.0.0", "log", - "pin-project-lite 0.2.0", + "pin-project-lite 0.2.4", "tracing-core", ] @@ -2811,7 +2737,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4189890526f0168710b6ee65ceaedf1460c48a14318ceec933cb26baa492096a" dependencies = [ - "linked-hash-map 0.5.3", + "linked-hash-map 0.5.4", ] [[package]] @@ -2831,9 +2757,9 @@ dependencies = [ [[package]] name = "unicode-normalization" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" dependencies = [ "tinyvec", ] @@ -2872,7 +2798,7 @@ dependencies = [ "idna", "matches", "percent-encoding", - "serde 1.0.118", + "serde 1.0.123", ] [[package]] @@ -2883,12 +2809,12 @@ checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7" [[package]] name = "uuid" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "rand", - "serde 1.0.118", + "getrandom 0.2.2", + "serde 1.0.123", ] [[package]] @@ -2917,9 +2843,9 @@ checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wasm-bindgen" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e" +checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -2927,9 +2853,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62" +checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7" dependencies = [ "bumpalo", "lazy_static", @@ -2942,9 +2868,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084" +checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2952,9 +2878,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549" +checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385" dependencies = [ "proc-macro2", "quote", @@ -2965,15 +2891,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.69" +version = "0.2.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158" +checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64" [[package]] name = "web-sys" -version = "0.3.46" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3" +checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3" dependencies = [ "js-sys", "wasm-bindgen", @@ -3080,11 +3006,11 @@ dependencies = [ [[package]] name = "yaml-rust" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ - "linked-hash-map 0.5.3", + "linked-hash-map 0.5.4", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 203c4ed..9fc9409 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "relay" description = "A simple activitypub relay" -version = "0.1.0" +version = "0.2.0" authors = ["asonix "] license-file = "LICENSE" readme = "README.md" @@ -15,9 +15,9 @@ build = "src/build.rs" [dependencies] anyhow = "1.0" actix-rt = "1.1.1" -actix-web = { version = "3.0.1", default-features = false, features = ["rustls", "compress"] } +actix-web = { version = "3.3.2", default-features = false, features = ["rustls", "compress"] } actix-webfinger = "0.3.0" -activitystreams = "0.7.0-alpha.4" +activitystreams = "0.7.0-alpha.9" activitystreams-ext = "0.1.0-alpha.2" ammonia = "3.1.0" async-mutex = "1.0.1" @@ -27,8 +27,6 @@ background-jobs = "0.8.0" base64 = "0.13" chrono = "0.4.19" config = "0.10.1" -deadpool = "0.5.1" -deadpool-postgres = "0.5.5" dotenv = "0.15.0" env_logger = "0.8.2" futures = "0.3.4" @@ -45,9 +43,9 @@ rsa-pem = "0.2.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" sha2 = "0.9" +sled = "0.34.6" structopt = "0.3.12" thiserror = "1.0" -tokio-postgres = { version = "0.5.1", features = ["with-serde_json-1", "with-uuid-0_8", "with-chrono-0_4"] } ttl_cache = "0.5.1" uuid = { version = "0.8", features = ["v4", "serde"] } diff --git a/docker/prod/deploy.sh b/docker/prod/deploy.sh index 8042ec0..2d4fc71 100755 --- a/docker/prod/deploy.sh +++ b/docker/prod/deploy.sh @@ -1,7 +1,6 @@ #!/usr/bin/env bash TAG=$1 -MIGRATIONS=$2 function require() { if [ "$1" = "" ]; then @@ -15,11 +14,10 @@ function print_help() { echo "build.sh" echo "" echo "Usage:" - echo " build.sh [tag] [migrations]" + echo " build.sh [tag]" echo "" echo "Args:" echo " tag: The git tag to create and publish" - echo " migrations: (optional) Whether to build the migrations container as well" } function build_image() { @@ -61,12 +59,3 @@ build_image "asonix/relay" "$TAG" "amd64" ./manifest.sh "asonix/relay" "$TAG" ./manifest.sh "asonix/relay" "latest" - -if [ "${MIGRATIONS}" = "migrations" ]; then - build_image "asonix/relay-migrations" "$TAG" arm64v8 - build_image "asonix/relay-migrations" "$TAG" arm32v7 - build_image "asonix/relay-migrations" "$TAG" amd64 - - ./manifest.sh "asonix/relay-migrations" "$TAG" - ./manifest.sh "asonix/relay-migrations" "latest" -fi diff --git a/migrations/.gitkeep b/migrations/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/migrations/00000000000000_diesel_initial_setup/down.sql b/migrations/00000000000000_diesel_initial_setup/down.sql deleted file mode 100644 index a9f5260..0000000 --- a/migrations/00000000000000_diesel_initial_setup/down.sql +++ /dev/null @@ -1,6 +0,0 @@ --- This file was automatically created by Diesel to setup helper functions --- and other internal bookkeeping. This file is safe to edit, any future --- changes will be added to existing projects as new migrations. - -DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); -DROP FUNCTION IF EXISTS diesel_set_updated_at(); diff --git a/migrations/00000000000000_diesel_initial_setup/up.sql b/migrations/00000000000000_diesel_initial_setup/up.sql deleted file mode 100644 index d68895b..0000000 --- a/migrations/00000000000000_diesel_initial_setup/up.sql +++ /dev/null @@ -1,36 +0,0 @@ --- This file was automatically created by Diesel to setup helper functions --- and other internal bookkeeping. This file is safe to edit, any future --- changes will be added to existing projects as new migrations. - - - - --- Sets up a trigger for the given table to automatically set a column called --- `updated_at` whenever the row is modified (unless `updated_at` was included --- in the modified columns) --- --- # Example --- --- ```sql --- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); --- --- SELECT diesel_manage_updated_at('users'); --- ``` -CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ -BEGIN - EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s - FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ -BEGIN - IF ( - NEW IS DISTINCT FROM OLD AND - NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at - ) THEN - NEW.updated_at := current_timestamp; - END IF; - RETURN NEW; -END; -$$ LANGUAGE plpgsql; diff --git a/migrations/2020-03-14-211045_create_listeners/down.sql b/migrations/2020-03-14-211045_create_listeners/down.sql deleted file mode 100644 index dde63ef..0000000 --- a/migrations/2020-03-14-211045_create_listeners/down.sql +++ /dev/null @@ -1,3 +0,0 @@ --- This file should undo anything in `up.sql` -DROP INDEX listeners_actor_id_index; -DROP TABLE listeners; diff --git a/migrations/2020-03-14-211045_create_listeners/up.sql b/migrations/2020-03-14-211045_create_listeners/up.sql deleted file mode 100644 index ee6324e..0000000 --- a/migrations/2020-03-14-211045_create_listeners/up.sql +++ /dev/null @@ -1,11 +0,0 @@ --- Your SQL goes here -CREATE TABLE listeners ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - actor_id TEXT UNIQUE NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP -); - -CREATE INDEX listeners_actor_id_index ON listeners(actor_id); - -SELECT diesel_manage_updated_at('listeners'); diff --git a/migrations/2020-03-14-213217_create_blocks/down.sql b/migrations/2020-03-14-213217_create_blocks/down.sql deleted file mode 100644 index c25fc5c..0000000 --- a/migrations/2020-03-14-213217_create_blocks/down.sql +++ /dev/null @@ -1,3 +0,0 @@ --- This file should undo anything in `up.sql` -DROP INDEX blocks_domain_name_index; -DROP TABLE blocks; diff --git a/migrations/2020-03-14-213217_create_blocks/up.sql b/migrations/2020-03-14-213217_create_blocks/up.sql deleted file mode 100644 index 364816b..0000000 --- a/migrations/2020-03-14-213217_create_blocks/up.sql +++ /dev/null @@ -1,11 +0,0 @@ --- Your SQL goes here -CREATE TABLE blocks ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - domain_name TEXT UNIQUE NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP -); - -CREATE INDEX blocks_domain_name_index ON blocks(domain_name); - -SELECT diesel_manage_updated_at('blocks'); diff --git a/migrations/2020-03-14-213511_create_whitelists/down.sql b/migrations/2020-03-14-213511_create_whitelists/down.sql deleted file mode 100644 index ee24697..0000000 --- a/migrations/2020-03-14-213511_create_whitelists/down.sql +++ /dev/null @@ -1,3 +0,0 @@ --- This file should undo anything in `up.sql` -DROP INDEX whitelists_domain_name_index; -DROP TABLE whitelists; diff --git a/migrations/2020-03-14-213511_create_whitelists/up.sql b/migrations/2020-03-14-213511_create_whitelists/up.sql deleted file mode 100644 index 5a2ab11..0000000 --- a/migrations/2020-03-14-213511_create_whitelists/up.sql +++ /dev/null @@ -1,11 +0,0 @@ --- Your SQL goes here -CREATE TABLE whitelists ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - domain_name TEXT UNIQUE NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP -); - -CREATE INDEX whitelists_domain_name_index ON whitelists(domain_name); - -SELECT diesel_manage_updated_at('whitelists'); diff --git a/migrations/2020-03-16-012053_create-settings/down.sql b/migrations/2020-03-16-012053_create-settings/down.sql deleted file mode 100644 index f1463d7..0000000 --- a/migrations/2020-03-16-012053_create-settings/down.sql +++ /dev/null @@ -1,3 +0,0 @@ --- This file should undo anything in `up.sql` -DROP INDEX settings_key_index; -DROP TABLE settings; diff --git a/migrations/2020-03-16-012053_create-settings/up.sql b/migrations/2020-03-16-012053_create-settings/up.sql deleted file mode 100644 index 9836a98..0000000 --- a/migrations/2020-03-16-012053_create-settings/up.sql +++ /dev/null @@ -1,12 +0,0 @@ --- Your SQL goes here -CREATE TABLE settings ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - key TEXT UNIQUE NOT NULL, - value TEXT NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP -); - -CREATE INDEX settings_key_index ON settings(key); - -SELECT diesel_manage_updated_at('settings'); diff --git a/migrations/2020-03-16-160336_add_triggers/down.sql b/migrations/2020-03-16-160336_add_triggers/down.sql deleted file mode 100644 index 8b8f803..0000000 --- a/migrations/2020-03-16-160336_add_triggers/down.sql +++ /dev/null @@ -1,8 +0,0 @@ --- This file should undo anything in `up.sql` -DROP TRIGGER IF EXISTS whitelists_notify ON whitelists; -DROP TRIGGER IF EXISTS blocks_notify ON blocks; -DROP TRIGGER IF EXISTS listeners_notify ON listeners; - -DROP FUNCTION IF EXISTS invoke_whitelists_trigger(); -DROP FUNCTION IF EXISTS invoke_blocks_trigger(); -DROP FUNCTION IF EXISTS invoke_listeners_trigger(); diff --git a/migrations/2020-03-16-160336_add_triggers/up.sql b/migrations/2020-03-16-160336_add_triggers/up.sql deleted file mode 100644 index 5520a34..0000000 --- a/migrations/2020-03-16-160336_add_triggers/up.sql +++ /dev/null @@ -1,99 +0,0 @@ --- Your SQL goes here -CREATE OR REPLACE FUNCTION invoke_listeners_trigger () - RETURNS TRIGGER - LANGUAGE plpgsql -AS $$ -DECLARE - rec RECORD; - channel TEXT; - payload TEXT; -BEGIN - case TG_OP - WHEN 'INSERT' THEN - rec := NEW; - channel := 'new_listeners'; - payload := NEW.actor_id; - WHEN 'DELETE' THEN - rec := OLD; - channel := 'rm_listeners'; - payload := OLD.actor_id; - ELSE - RAISE EXCEPTION 'Unknown TG_OP: "%". Should not occur!', TG_OP; - END CASE; - - PERFORM pg_notify(channel, payload::TEXT); - RETURN rec; -END; -$$; - -CREATE OR REPLACE FUNCTION invoke_blocks_trigger () - RETURNS TRIGGER - LANGUAGE plpgsql -AS $$ -DECLARE - rec RECORD; - channel TEXT; - payload TEXT; -BEGIN - case TG_OP - WHEN 'INSERT' THEN - rec := NEW; - channel := 'new_blocks'; - payload := NEW.domain_name; - WHEN 'DELETE' THEN - rec := OLD; - channel := 'rm_blocks'; - payload := OLD.domain_name; - ELSE - RAISE EXCEPTION 'Unknown TG_OP: "%". Should not occur!', TG_OP; - END CASE; - - PERFORM pg_notify(channel, payload::TEXT); - RETURN NULL; -END; -$$; - -CREATE OR REPLACE FUNCTION invoke_whitelists_trigger () - RETURNS TRIGGER - LANGUAGE plpgsql -AS $$ -DECLARE - rec RECORD; - channel TEXT; - payload TEXT; -BEGIN - case TG_OP - WHEN 'INSERT' THEN - rec := NEW; - channel := 'new_whitelists'; - payload := NEW.domain_name; - WHEN 'DELETE' THEN - rec := OLD; - channel := 'rm_whitelists'; - payload := OLD.domain_name; - ELSE - RAISE EXCEPTION 'Unknown TG_OP: "%". Should not occur!', TG_OP; - END CASE; - - PERFORM pg_notify(channel, payload::TEXT); - RETURN rec; -END; -$$; - -CREATE TRIGGER listeners_notify - AFTER INSERT OR UPDATE OR DELETE - ON listeners -FOR EACH ROW - EXECUTE PROCEDURE invoke_listeners_trigger(); - -CREATE TRIGGER blocks_notify - AFTER INSERT OR UPDATE OR DELETE - ON blocks -FOR EACH ROW - EXECUTE PROCEDURE invoke_blocks_trigger(); - -CREATE TRIGGER whitelists_notify - AFTER INSERT OR UPDATE OR DELETE - ON whitelists -FOR EACH ROW - EXECUTE PROCEDURE invoke_whitelists_trigger(); diff --git a/migrations/2020-03-22-194453_create-jobs/down.sql b/migrations/2020-03-22-194453_create-jobs/down.sql deleted file mode 100644 index 8774d0f..0000000 --- a/migrations/2020-03-22-194453_create-jobs/down.sql +++ /dev/null @@ -1,3 +0,0 @@ --- This file should undo anything in `up.sql` -DROP INDEX jobs_queue_status_index; -DROP TABLE jobs; diff --git a/migrations/2020-03-22-194453_create-jobs/up.sql b/migrations/2020-03-22-194453_create-jobs/up.sql deleted file mode 100644 index 2504d28..0000000 --- a/migrations/2020-03-22-194453_create-jobs/up.sql +++ /dev/null @@ -1,17 +0,0 @@ --- Your SQL goes here -CREATE TABLE jobs ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - job_id UUID UNIQUE NOT NULL, - job_queue TEXT NOT NULL, - job_timeout BIGINT NOT NULL, - job_updated TIMESTAMP NOT NULL, - job_status TEXT NOT NULL, - job_value JSONB NOT NULL, - job_next_run TIMESTAMP, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL DEFAULT NOW() -); - -CREATE INDEX jobs_queue_status_index ON jobs(job_queue, job_status); - -SELECT diesel_manage_updated_at('jobs'); diff --git a/migrations/2020-03-23-175108_create-actors/down.sql b/migrations/2020-03-23-175108_create-actors/down.sql deleted file mode 100644 index 5d31fbb..0000000 --- a/migrations/2020-03-23-175108_create-actors/down.sql +++ /dev/null @@ -1,4 +0,0 @@ --- This file should undo anything in `up.sql` -DROP TRIGGER IF EXISTS actors_notify ON actors; -DROP FUNCTION IF EXISTS invoke_actors_trigger(); -DROP TABLE actors; diff --git a/migrations/2020-03-23-175108_create-actors/up.sql b/migrations/2020-03-23-175108_create-actors/up.sql deleted file mode 100644 index bbd921c..0000000 --- a/migrations/2020-03-23-175108_create-actors/up.sql +++ /dev/null @@ -1,49 +0,0 @@ --- Your SQL goes here -CREATE TABLE actors ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - actor_id TEXT UNIQUE NOT NULL, - public_key TEXT NOT NULL, - public_key_id TEXT UNIQUE NOT NULL, - listener_id UUID NOT NULL REFERENCES listeners(id) ON DELETE CASCADE, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL DEFAULT NOW() -); - -SELECT diesel_manage_updated_at('actors'); - -CREATE OR REPLACE FUNCTION invoke_actors_trigger () - RETURNS TRIGGER - LANGUAGE plpgsql -AS $$ -DECLARE - rec RECORD; - channel TEXT; - payload TEXT; -BEGIN - case TG_OP - WHEN 'INSERT' THEN - rec := NEW; - channel := 'new_actors'; - payload := NEW.actor_id; - WHEN 'UPDATE' THEN - rec := NEW; - channel := 'new_actors'; - payload := NEW.actor_id; - WHEN 'DELETE' THEN - rec := OLD; - channel := 'rm_actors'; - payload := OLD.actor_id; - ELSE - RAISE EXCEPTION 'Unknown TG_OP: "%". Should not occur!', TG_OP; - END CASE; - - PERFORM pg_notify(channel, payload::TEXT); - RETURN rec; -END; -$$; - -CREATE TRIGGER actors_notify - AFTER INSERT OR UPDATE OR DELETE - ON actors -FOR EACH ROW - EXECUTE PROCEDURE invoke_actors_trigger(); diff --git a/migrations/2020-03-23-175637_create-nodes/down.sql b/migrations/2020-03-23-175637_create-nodes/down.sql deleted file mode 100644 index 7d3304f..0000000 --- a/migrations/2020-03-23-175637_create-nodes/down.sql +++ /dev/null @@ -1,2 +0,0 @@ --- This file should undo anything in `up.sql` -DROP TABLE nodes; diff --git a/migrations/2020-03-23-175637_create-nodes/up.sql b/migrations/2020-03-23-175637_create-nodes/up.sql deleted file mode 100644 index a821864..0000000 --- a/migrations/2020-03-23-175637_create-nodes/up.sql +++ /dev/null @@ -1,12 +0,0 @@ --- Your SQL goes here -CREATE TABLE nodes ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - listener_id UUID NOT NULL REFERENCES listeners(id) ON DELETE CASCADE, - nodeinfo JSONB, - instance JSONB, - contact JSONB, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL DEFAULT NOW() -); - -SELECT diesel_manage_updated_at('nodes'); diff --git a/migrations/2020-03-25-211630_add-node-notifications/down.sql b/migrations/2020-03-25-211630_add-node-notifications/down.sql deleted file mode 100644 index b990082..0000000 --- a/migrations/2020-03-25-211630_add-node-notifications/down.sql +++ /dev/null @@ -1,3 +0,0 @@ --- This file should undo anything in `up.sql` -DROP TRIGGER IF EXISTS nodes_notify ON nodes; -DROP FUNCTION IF EXISTS invoke_nodes_trigger(); diff --git a/migrations/2020-03-25-211630_add-node-notifications/up.sql b/migrations/2020-03-25-211630_add-node-notifications/up.sql deleted file mode 100644 index 018a89f..0000000 --- a/migrations/2020-03-25-211630_add-node-notifications/up.sql +++ /dev/null @@ -1,37 +0,0 @@ --- Your SQL goes here -CREATE OR REPLACE FUNCTION invoke_nodes_trigger () - RETURNS TRIGGER - LANGUAGE plpgsql -AS $$ -DECLARE - rec RECORD; - channel TEXT; - payload TEXT; -BEGIN - case TG_OP - WHEN 'INSERT' THEN - rec := NEW; - channel := 'new_nodes'; - payload := NEW.listener_id; - WHEN 'UPDATE' THEN - rec := NEW; - channel := 'new_nodes'; - payload := NEW.listener_id; - WHEN 'DELETE' THEN - rec := OLD; - channel := 'rm_nodes'; - payload := OLD.listener_id; - ELSE - RAISE EXCEPTION 'Unknown TG_OP: "%". Should not occur!', TG_OP; - END CASE; - - PERFORM pg_notify(channel, payload::TEXT); - RETURN rec; -END; -$$; - -CREATE TRIGGER nodes_notify - AFTER INSERT OR UPDATE OR DELETE - ON nodes -FOR EACH ROW - EXECUTE PROCEDURE invoke_nodes_trigger(); diff --git a/migrations/2020-03-25-214244_make-nodes-listener-id-unique/down.sql b/migrations/2020-03-25-214244_make-nodes-listener-id-unique/down.sql deleted file mode 100644 index 4d8057e..0000000 --- a/migrations/2020-03-25-214244_make-nodes-listener-id-unique/down.sql +++ /dev/null @@ -1,2 +0,0 @@ --- This file should undo anything in `up.sql` -ALTER TABLE nodes DROP CONSTRAINT nodes_listener_ids_unique; diff --git a/migrations/2020-03-25-214244_make-nodes-listener-id-unique/up.sql b/migrations/2020-03-25-214244_make-nodes-listener-id-unique/up.sql deleted file mode 100644 index 2cd4967..0000000 --- a/migrations/2020-03-25-214244_make-nodes-listener-id-unique/up.sql +++ /dev/null @@ -1,2 +0,0 @@ --- Your SQL goes here -ALTER TABLE nodes ADD CONSTRAINT nodes_listener_ids_unique UNIQUE (listener_id); diff --git a/migrations/2020-03-26-163641_create-media/down.sql b/migrations/2020-03-26-163641_create-media/down.sql deleted file mode 100644 index a1e615c..0000000 --- a/migrations/2020-03-26-163641_create-media/down.sql +++ /dev/null @@ -1,2 +0,0 @@ --- This file should undo anything in `up.sql` -DROP TABLE media; diff --git a/migrations/2020-03-26-163641_create-media/up.sql b/migrations/2020-03-26-163641_create-media/up.sql deleted file mode 100644 index 8346739..0000000 --- a/migrations/2020-03-26-163641_create-media/up.sql +++ /dev/null @@ -1,10 +0,0 @@ --- Your SQL goes here -CREATE TABLE media ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - media_id UUID UNIQUE NOT NULL, - url TEXT UNIQUE NOT NULL, - created_at TIMESTAMP NOT NULL, - updated_at TIMESTAMP NOT NULL DEFAULT NOW() -); - -SELECT diesel_manage_updated_at('media'); diff --git a/src/args.rs b/src/args.rs index 952cd7a..4a90602 100644 --- a/src/args.rs +++ b/src/args.rs @@ -6,25 +6,11 @@ pub struct Args { #[structopt(short, help = "A list of domains that should be blocked")] blocks: Vec, - #[structopt(short, help = "A list of domains that should be whitelisted")] - whitelists: Vec, + #[structopt(short, help = "A list of domains that should be allowed")] + allowed: Vec, - #[structopt(short, long, help = "Undo whitelisting or blocking domains")] + #[structopt(short, long, help = "Undo allowing or blocking domains")] undo: bool, - - #[structopt( - short, - long, - help = "Only process background jobs, do not start the relay server" - )] - jobs_only: bool, - - #[structopt( - short, - long, - help = "Only run the relay server, do not process background jobs" - )] - no_jobs: bool, } impl Args { @@ -36,19 +22,11 @@ impl Args { &self.blocks } - pub fn whitelists(&self) -> &[String] { - &self.whitelists + pub fn allowed(&self) -> &[String] { + &self.allowed } pub fn undo(&self) -> bool { self.undo } - - pub fn jobs_only(&self) -> bool { - self.jobs_only - } - - pub fn no_jobs(&self) -> bool { - self.no_jobs - } } diff --git a/src/config.rs b/src/config.rs index 2a7dd5b..877dd1c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,7 +8,7 @@ use activitystreams::{uri, url::Url}; use config::Environment; use http_signature_normalization_actix::prelude::{VerifyDigest, VerifySignature}; use sha2::{Digest, Sha256}; -use std::net::IpAddr; +use std::{net::IpAddr, path::PathBuf}; use uuid::Uuid; #[derive(Clone, Debug, serde::Deserialize)] @@ -17,13 +17,14 @@ pub struct ParsedConfig { addr: IpAddr, port: u16, debug: bool, - whitelist_mode: bool, + restricted_mode: bool, validate_signatures: bool, https: bool, database_url: String, pretty_log: bool, publish_blocks: bool, max_connections: usize, + sled_path: PathBuf, } #[derive(Clone, Debug)] @@ -32,13 +33,14 @@ pub struct Config { addr: IpAddr, port: u16, debug: bool, - whitelist_mode: bool, + restricted_mode: bool, validate_signatures: bool, database_url: String, pretty_log: bool, publish_blocks: bool, max_connections: usize, base_uri: Url, + sled_path: PathBuf, } pub enum UrlKind { @@ -62,12 +64,13 @@ impl Config { .set_default("addr", "127.0.0.1")? .set_default("port", 8080)? .set_default("debug", true)? - .set_default("whitelist_mode", false)? + .set_default("restricted_mode", false)? .set_default("validate_signatures", false)? .set_default("https", false)? .set_default("pretty_log", true)? .set_default("publish_blocks", false)? .set_default("max_connections", 2)? + .set_default("sled_path", "./sled/db-0-34")? .merge(Environment::new())?; let config: ParsedConfig = config.try_into()?; @@ -80,16 +83,21 @@ impl Config { addr: config.addr, port: config.port, debug: config.debug, - whitelist_mode: config.whitelist_mode, + restricted_mode: config.restricted_mode, validate_signatures: config.validate_signatures, database_url: config.database_url, pretty_log: config.pretty_log, publish_blocks: config.publish_blocks, max_connections: config.max_connections, base_uri, + sled_path: config.sled_path, }) } + pub fn sled_path(&self) -> &PathBuf { + &self.sled_path + } + pub fn pretty_log(&self) -> bool { self.pretty_log } @@ -135,8 +143,8 @@ impl Config { self.publish_blocks } - pub fn whitelist_mode(&self) -> bool { - self.whitelist_mode + pub fn restricted_mode(&self) -> bool { + self.restricted_mode } pub fn database_url(&self) -> &str { @@ -156,7 +164,7 @@ impl Config { } pub fn software_version(&self) -> String { - "v0.1.0-main".to_owned() + "v0.2.0-main".to_owned() } pub fn source_code(&self) -> String { diff --git a/src/data/actor.rs b/src/data/actor.rs index b9cdc64..54c24b8 100644 --- a/src/data/actor.rs +++ b/src/data/actor.rs @@ -1,10 +1,11 @@ -use crate::{apub::AcceptedActors, db::Db, error::MyError, requests::Requests}; -use activitystreams::{prelude::*, uri, url::Url}; -use async_rwlock::RwLock; -use log::error; -use std::{collections::HashSet, sync::Arc, time::Duration}; -use ttl_cache::TtlCache; -use uuid::Uuid; +use crate::{ + apub::AcceptedActors, + db::{Actor, Db}, + error::MyError, + requests::Requests, +}; +use activitystreams::{prelude::*, url::Url}; +use std::time::{Duration, SystemTime}; const REFETCH_DURATION: Duration = Duration::from_secs(60 * 30); @@ -15,14 +16,14 @@ pub enum MaybeCached { } impl MaybeCached { - pub fn is_cached(&self) -> bool { + pub(crate) fn is_cached(&self) -> bool { match self { MaybeCached::Cached(_) => true, _ => false, } } - pub fn into_inner(self) -> T { + pub(crate) fn into_inner(self) -> T { match self { MaybeCached::Cached(t) | MaybeCached::Fetched(t) => t, } @@ -32,28 +33,43 @@ impl MaybeCached { #[derive(Clone)] pub struct ActorCache { db: Db, - cache: Arc>>, - following: Arc>>, } impl ActorCache { - pub fn new(db: Db) -> Self { - let cache = ActorCache { - db, - cache: Arc::new(RwLock::new(TtlCache::new(1024 * 8))), - following: Arc::new(RwLock::new(HashSet::new())), - }; - - cache.spawn_rehydrate(); - - cache + pub(crate) fn new(db: Db) -> Self { + ActorCache { db } } - pub async fn is_following(&self, id: &Url) -> bool { - self.following.read().await.contains(id) + pub(crate) async fn get( + &self, + id: &Url, + requests: &Requests, + ) -> Result, MyError> { + if let Some(actor) = self.db.actor(id.clone()).await? { + if actor.saved_at + REFETCH_DURATION > SystemTime::now() { + return Ok(MaybeCached::Cached(actor)); + } + } + + self.get_no_cache(id, requests) + .await + .map(MaybeCached::Fetched) } - pub async fn get_no_cache(&self, id: &Url, requests: &Requests) -> Result { + pub(crate) async fn follower(&self, actor: Actor) -> Result<(), MyError> { + self.db.add_listener(actor.id.clone()).await?; + self.db.save_actor(actor).await + } + + pub(crate) async fn unfollower(&self, actor: &Actor) -> Result<(), MyError> { + self.db.remove_listener(actor.id.clone()).await + } + + pub(crate) async fn get_no_cache( + &self, + id: &Url, + requests: &Requests, + ) -> Result { let accepted_actor = requests.fetch::(id.as_str()).await?; let input_domain = id.domain().ok_or(MyError::MissingDomain)?; @@ -68,244 +84,13 @@ impl ActorCache { public_key: accepted_actor.ext_one.public_key.public_key_pem, public_key_id: accepted_actor.ext_one.public_key.id, inbox: inbox.into(), + saved_at: SystemTime::now(), }; - self.cache - .write() - .await - .insert(id.clone(), actor.clone(), REFETCH_DURATION); - - self.update(id, &actor.public_key, &actor.public_key_id) - .await?; + self.db.save_actor(actor.clone()).await?; Ok(actor) } - - pub async fn get(&self, id: &Url, requests: &Requests) -> Result, MyError> { - if let Some(actor) = self.cache.read().await.get(id) { - return Ok(MaybeCached::Cached(actor.clone())); - } - - if let Some(actor) = self.lookup(id).await? { - self.cache - .write() - .await - .insert(id.clone(), actor.clone(), REFETCH_DURATION); - return Ok(MaybeCached::Cached(actor)); - } - - self.get_no_cache(id, requests) - .await - .map(MaybeCached::Fetched) - } - - pub async fn follower(&self, actor: &Actor) -> Result<(), MyError> { - self.save(actor.clone()).await - } - - pub async fn cache_follower(&self, id: Url) { - self.following.write().await.insert(id); - } - - pub async fn bust_follower(&self, id: &Url) { - self.following.write().await.remove(id); - } - - pub async fn unfollower(&self, actor: &Actor) -> Result, MyError> { - let row_opt = self - .db - .pool() - .get() - .await? - .query_opt( - "DELETE FROM actors - WHERE actor_id = $1::TEXT - RETURNING listener_id;", - &[&actor.id.as_str()], - ) - .await?; - - let row = if let Some(row) = row_opt { - row - } else { - return Ok(None); - }; - - let listener_id: Uuid = row.try_get(0)?; - - let row_opt = self - .db - .pool() - .get() - .await? - .query_opt( - "SELECT FROM actors - WHERE listener_id = $1::UUID;", - &[&listener_id], - ) - .await?; - - if row_opt.is_none() { - return Ok(Some(listener_id)); - } - - Ok(None) - } - - async fn lookup(&self, id: &Url) -> Result, MyError> { - let row_opt = self - .db - .pool() - .get() - .await? - .query_opt( - "SELECT listeners.actor_id, actors.public_key, actors.public_key_id - FROM listeners - INNER JOIN actors ON actors.listener_id = listeners.id - WHERE - actors.actor_id = $1::TEXT - AND - actors.updated_at + INTERVAL '120 seconds' < NOW() - LIMIT 1;", - &[&id.as_str()], - ) - .await?; - - let row = if let Some(row) = row_opt { - row - } else { - return Ok(None); - }; - - let inbox: String = row.try_get(0)?; - let public_key_id: String = row.try_get(2)?; - - Ok(Some(Actor { - id: id.clone().into(), - inbox: uri!(inbox).into(), - public_key: row.try_get(1)?, - public_key_id: uri!(public_key_id).into(), - })) - } - - async fn save(&self, actor: Actor) -> Result<(), MyError> { - let row_opt = self - .db - .pool() - .get() - .await? - .query_opt( - "SELECT id FROM listeners WHERE actor_id = $1::TEXT LIMIT 1;", - &[&actor.inbox.as_str()], - ) - .await?; - - let row = if let Some(row) = row_opt { - row - } else { - return Err(MyError::NotSubscribed(actor.id.as_str().to_owned())); - }; - - let listener_id: Uuid = row.try_get(0)?; - - self.db - .pool() - .get() - .await? - .execute( - "INSERT INTO actors ( - actor_id, - public_key, - public_key_id, - listener_id, - created_at, - updated_at - ) VALUES ( - $1::TEXT, - $2::TEXT, - $3::TEXT, - $4::UUID, - 'now', - 'now' - ) ON CONFLICT (actor_id) - DO UPDATE SET public_key = $2::TEXT;", - &[ - &actor.id.as_str(), - &actor.public_key, - &actor.public_key_id.as_str(), - &listener_id, - ], - ) - .await?; - Ok(()) - } - - async fn update(&self, id: &Url, public_key: &str, public_key_id: &Url) -> Result<(), MyError> { - self.db - .pool() - .get() - .await? - .execute( - "UPDATE actors - SET public_key = $2::TEXT, public_key_id = $3::TEXT - WHERE actor_id = $1::TEXT;", - &[&id.as_str(), &public_key, &public_key_id.as_str()], - ) - .await?; - - Ok(()) - } - - fn spawn_rehydrate(&self) { - use actix_rt::time::{interval_at, Instant}; - - let this = self.clone(); - actix_rt::spawn(async move { - let mut interval = interval_at(Instant::now(), Duration::from_secs(60 * 10)); - - loop { - if let Err(e) = this.rehydrate().await { - error!("Error rehydrating follows, {}", e); - } - - interval.tick().await; - } - }); - } - - async fn rehydrate(&self) -> Result<(), MyError> { - let rows = self - .db - .pool() - .get() - .await? - .query("SELECT actor_id FROM actors;", &[]) - .await?; - - let actor_ids = rows - .into_iter() - .filter_map(|row| match row.try_get(0) { - Ok(s) => { - let s: String = s; - match s.parse() { - Ok(s) => Some(s), - Err(e) => { - error!("Error parsing actor id, {}", e); - None - } - } - } - Err(e) => { - error!("Error getting actor id from row, {}", e); - None - } - }) - .collect(); - - let mut write_guard = self.following.write().await; - *write_guard = actor_ids; - Ok(()) - } } fn get_inbox(actor: &AcceptedActors) -> Result<&Url, MyError> { @@ -314,11 +99,3 @@ fn get_inbox(actor: &AcceptedActors) -> Result<&Url, MyError> { .and_then(|e| e.shared_inbox) .unwrap_or(actor.inbox()?)) } - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -pub struct Actor { - pub id: Url, - pub public_key: String, - pub public_key_id: Url, - pub inbox: Url, -} diff --git a/src/data/media.rs b/src/data/media.rs index a8fcfae..3907b56 100644 --- a/src/data/media.rs +++ b/src/data/media.rs @@ -1,171 +1,79 @@ -use crate::{db::Db, error::MyError}; +use crate::{ + db::{Db, MediaMeta}, + error::MyError, +}; use activitystreams::url::Url; use actix_web::web::Bytes; -use async_mutex::Mutex; -use async_rwlock::RwLock; -use futures::join; -use lru::LruCache; -use std::{collections::HashMap, sync::Arc, time::Duration}; -use ttl_cache::TtlCache; +use std::time::{Duration, SystemTime}; use uuid::Uuid; static MEDIA_DURATION: Duration = Duration::from_secs(60 * 60 * 24 * 2); #[derive(Clone)] -pub struct Media { +pub struct MediaCache { db: Db, - inverse: Arc>>, - url_cache: Arc>>, - byte_cache: Arc>>, } -impl Media { +impl MediaCache { pub fn new(db: Db) -> Self { - Media { - db, - inverse: Arc::new(Mutex::new(HashMap::new())), - url_cache: Arc::new(Mutex::new(LruCache::new(128))), - byte_cache: Arc::new(RwLock::new(TtlCache::new(128))), - } + MediaCache { db } } - pub async fn get_uuid(&self, url: &Url) -> Result, MyError> { - let res = self.inverse.lock().await.get(url).cloned(); - let uuid = match res { - Some(uuid) => uuid, - _ => { - let row_opt = self - .db - .pool() - .get() - .await? - .query_opt( - "SELECT media_id - FROM media - WHERE url = $1::TEXT - LIMIT 1;", - &[&url.as_str()], - ) - .await?; - - if let Some(row) = row_opt { - let uuid: Uuid = row.try_get(0)?; - self.inverse.lock().await.insert(url.clone(), uuid); - uuid - } else { - return Ok(None); - } - } - }; - - if self.url_cache.lock().await.contains(&uuid) { - return Ok(Some(uuid)); - } - - let row_opt = self - .db - .pool() - .get() - .await? - .query_opt( - "SELECT id - FROM media - WHERE - url = $1::TEXT - AND - media_id = $2::UUID - LIMIT 1;", - &[&url.as_str(), &uuid], - ) - .await?; - - if row_opt.is_some() { - self.url_cache.lock().await.put(uuid, url.clone()); - - return Ok(Some(uuid)); - } - - self.inverse.lock().await.remove(url); - - Ok(None) + pub async fn get_uuid(&self, url: Url) -> Result, MyError> { + self.db.media_id(url).await } pub async fn get_url(&self, uuid: Uuid) -> Result, MyError> { - if let Some(url) = self.url_cache.lock().await.get(&uuid).cloned() { - return Ok(Some(url)); + self.db.media_url(uuid).await + } + + pub async fn is_outdated(&self, uuid: Uuid) -> Result { + if let Some(meta) = self.db.media_meta(uuid).await? { + if meta.saved_at + MEDIA_DURATION > SystemTime::now() { + return Ok(false); + } } - let row_opt = self - .db - .pool() - .get() - .await? - .query_opt( - "SELECT url - FROM media - WHERE media_id = $1::UUID - LIMIT 1;", - &[&uuid], - ) - .await?; + Ok(true) + } - if let Some(row) = row_opt { - let url: String = row.try_get(0)?; - let url: Url = url.parse()?; - return Ok(Some(url)); + pub async fn get_bytes(&self, uuid: Uuid) -> Result, MyError> { + if let Some(meta) = self.db.media_meta(uuid).await? { + if meta.saved_at + MEDIA_DURATION > SystemTime::now() { + return self + .db + .media_bytes(uuid) + .await + .map(|opt| opt.map(|bytes| (meta.media_type, bytes))); + } } Ok(None) } - pub async fn get_bytes(&self, uuid: Uuid) -> Option<(String, Bytes)> { - self.byte_cache.read().await.get(&uuid).cloned() - } - - pub async fn store_url(&self, url: &Url) -> Result { + pub async fn store_url(&self, url: Url) -> Result { let uuid = Uuid::new_v4(); - let (_, _, res) = join!( - async { - self.inverse.lock().await.insert(url.clone(), uuid); - }, - async { - self.url_cache.lock().await.put(uuid, url.clone()); - }, - async { - self.db - .pool() - .get() - .await? - .execute( - "INSERT INTO media ( - media_id, - url, - created_at, - updated_at - ) VALUES ( - $1::UUID, - $2::TEXT, - 'now', - 'now' - ) ON CONFLICT (media_id) - DO UPDATE SET url = $2::TEXT;", - &[&uuid, &url.as_str()], - ) - .await?; - Ok(()) as Result<(), MyError> - } - ); - - res?; + self.db.save_url(url, uuid).await?; Ok(uuid) } - pub async fn store_bytes(&self, uuid: Uuid, content_type: String, bytes: Bytes) { - self.byte_cache - .write() + pub async fn store_bytes( + &self, + uuid: Uuid, + media_type: String, + bytes: Bytes, + ) -> Result<(), MyError> { + self.db + .save_bytes( + uuid, + MediaMeta { + media_type, + saved_at: SystemTime::now(), + }, + bytes, + ) .await - .insert(uuid, (content_type, bytes), MEDIA_DURATION); } } diff --git a/src/data/mod.rs b/src/data/mod.rs index dd373bf..918bdf7 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -3,9 +3,7 @@ mod media; mod node; mod state; -pub use self::{ - actor::{Actor, ActorCache}, - media::Media, - node::{Contact, Info, Instance, Node, NodeCache}, - state::State, -}; +pub(crate) use actor::ActorCache; +pub(crate) use media::MediaCache; +pub(crate) use node::{Node, NodeCache}; +pub(crate) use state::State; diff --git a/src/data/node.rs b/src/data/node.rs index eb449c7..c9da438 100644 --- a/src/data/node.rs +++ b/src/data/node.rs @@ -1,341 +1,146 @@ -use crate::{db::Db, error::MyError}; -use activitystreams::{uri, url::Url}; -use async_rwlock::RwLock; -use log::{debug, error}; -use std::{ - collections::{HashMap, HashSet}, - sync::Arc, - time::{Duration, SystemTime}, +use crate::{ + db::{Contact, Db, Info, Instance}, + error::MyError, }; -use tokio_postgres::types::Json; -use uuid::Uuid; - -pub type ListenersCache = Arc>>; +use activitystreams::url::Url; +use std::time::{Duration, SystemTime}; #[derive(Clone)] pub struct NodeCache { db: Db, - listeners: ListenersCache, - nodes: Arc>>, +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct Node { + pub(crate) base: Url, + pub(crate) info: Option, + pub(crate) instance: Option, + pub(crate) contact: Option, } impl NodeCache { - pub fn new(db: Db, listeners: ListenersCache) -> Self { - NodeCache { - db, - listeners, - nodes: Arc::new(RwLock::new(HashMap::new())), - } + pub(crate) fn new(db: Db) -> Self { + NodeCache { db } } - pub async fn nodes(&self) -> Vec { - let listeners: HashSet<_> = self.listeners.read().await.clone(); + pub(crate) async fn nodes(&self) -> Result, MyError> { + let infos = self.db.connected_info().await?; + let instances = self.db.connected_instance().await?; + let contacts = self.db.connected_contact().await?; - self.nodes - .read() - .await - .iter() - .filter_map(|(k, v)| { - if listeners.contains(k) { - Some(v.clone()) - } else { - None - } + let vec = self + .db + .connected_ids() + .await? + .into_iter() + .map(move |actor_id| { + let info = infos.get(&actor_id).map(|info| info.clone()); + let instance = instances.get(&actor_id).map(|instance| instance.clone()); + let contact = contacts.get(&actor_id).map(|contact| contact.clone()); + + Node::new(actor_id) + .info(info) + .instance(instance) + .contact(contact) }) - .collect() + .collect(); + + Ok(vec) } - pub async fn is_nodeinfo_outdated(&self, listener: &Url) -> bool { - let read_guard = self.nodes.read().await; - - let node = match read_guard.get(listener) { - None => { - debug!("No node for listener {}", listener); - return true; - } - Some(node) => node, - }; - - match node.info.as_ref() { - Some(nodeinfo) => nodeinfo.outdated(), - None => { - debug!("No info for node {}", node.base); - true - } - } + pub(crate) async fn is_nodeinfo_outdated(&self, actor_id: Url) -> bool { + self.db + .info(actor_id) + .await + .map(|opt| opt.map(|info| info.outdated()).unwrap_or(true)) + .unwrap_or(true) } - pub async fn is_contact_outdated(&self, listener: &Url) -> bool { - let read_guard = self.nodes.read().await; - - let node = match read_guard.get(listener) { - None => { - debug!("No node for listener {}", listener); - return true; - } - Some(node) => node, - }; - - match node.contact.as_ref() { - Some(contact) => contact.outdated(), - None => { - debug!("No contact for node {}", node.base); - true - } - } + pub(crate) async fn is_contact_outdated(&self, actor_id: Url) -> bool { + self.db + .contact(actor_id) + .await + .map(|opt| opt.map(|contact| contact.outdated()).unwrap_or(true)) + .unwrap_or(true) } - pub async fn is_instance_outdated(&self, listener: &Url) -> bool { - let read_guard = self.nodes.read().await; - - let node = match read_guard.get(listener) { - None => { - debug!("No node for listener {}", listener); - return true; - } - Some(node) => node, - }; - - match node.instance.as_ref() { - Some(instance) => instance.outdated(), - None => { - debug!("No instance for node {}", node.base); - true - } - } + pub(crate) async fn is_instance_outdated(&self, actor_id: Url) -> bool { + self.db + .instance(actor_id) + .await + .map(|opt| opt.map(|instance| instance.outdated()).unwrap_or(true)) + .unwrap_or(true) } - pub async fn cache_by_id(&self, id: Uuid) { - if let Err(e) = self.do_cache_by_id(id).await { - error!("Error loading node into cache, {}", e); - } - } - - pub async fn bust_by_id(&self, id: Uuid) { - if let Err(e) = self.do_bust_by_id(id).await { - error!("Error busting node cache, {}", e); - } - } - - async fn do_bust_by_id(&self, id: Uuid) -> Result<(), MyError> { - let row_opt = self - .db - .pool() - .get() - .await? - .query_opt( - "SELECT ls.actor_id - FROM listeners AS ls - INNER JOIN nodes AS nd ON nd.listener_id = ls.id - WHERE nd.id = $1::UUID - LIMIT 1;", - &[&id], - ) - .await?; - - let row = if let Some(row) = row_opt { - row - } else { - return Ok(()); - }; - - let listener: String = row.try_get(0)?; - - self.nodes.write().await.remove(&uri!(listener)); - - Ok(()) - } - - async fn do_cache_by_id(&self, id: Uuid) -> Result<(), MyError> { - let row_opt = self - .db - .pool() - .get() - .await? - .query_opt( - "SELECT ls.actor_id, nd.nodeinfo, nd.instance, nd.contact - FROM nodes AS nd - INNER JOIN listeners AS ls ON nd.listener_id = ls.id - WHERE nd.id = $1::UUID - LIMIT 1;", - &[&id], - ) - .await?; - - let row = if let Some(row) = row_opt { - row - } else { - return Ok(()); - }; - - let listener: String = row.try_get(0)?; - let listener = uri!(listener); - let info: Option> = row.try_get(1)?; - let instance: Option> = row.try_get(2)?; - let contact: Option> = row.try_get(3)?; - - { - let mut write_guard = self.nodes.write().await; - let node = write_guard - .entry(listener.clone()) - .or_insert_with(|| Node::new(listener)); - - if let Some(info) = info { - node.info = Some(info.0); - } - if let Some(instance) = instance { - node.instance = Some(instance.0); - } - if let Some(contact) = contact { - node.contact = Some(contact.0); - } - } - - Ok(()) - } - - pub async fn set_info( + pub(crate) async fn set_info( &self, - listener: &Url, + actor_id: Url, software: String, version: String, reg: bool, ) -> Result<(), MyError> { - if !self.listeners.read().await.contains(listener) { - let mut nodes = self.nodes.write().await; - nodes.remove(listener); - return Ok(()); - } - - let node = { - let mut write_guard = self.nodes.write().await; - let node = write_guard - .entry(listener.clone()) - .or_insert_with(|| Node::new(listener.clone())); - node.set_info(software, version, reg); - node.clone() - }; - self.save(listener, &node).await?; - Ok(()) + self.db + .save_info( + actor_id, + Info { + software, + version, + reg, + updated: SystemTime::now(), + }, + ) + .await } - pub async fn set_instance( + pub(crate) async fn set_instance( &self, - listener: &Url, + actor_id: Url, title: String, description: String, version: String, reg: bool, requires_approval: bool, ) -> Result<(), MyError> { - if !self.listeners.read().await.contains(listener) { - let mut nodes = self.nodes.write().await; - nodes.remove(listener); - return Ok(()); - } - - let node = { - let mut write_guard = self.nodes.write().await; - let node = write_guard - .entry(listener.clone()) - .or_insert_with(|| Node::new(listener.clone())); - node.set_instance(title, description, version, reg, requires_approval); - node.clone() - }; - self.save(listener, &node).await?; - Ok(()) + self.db + .save_instance( + actor_id, + Instance { + title, + description, + version, + reg, + requires_approval, + updated: SystemTime::now(), + }, + ) + .await } - pub async fn set_contact( + pub(crate) async fn set_contact( &self, - listener: &Url, + actor_id: Url, username: String, display_name: String, url: Url, avatar: Url, ) -> Result<(), MyError> { - if !self.listeners.read().await.contains(listener) { - let mut nodes = self.nodes.write().await; - nodes.remove(listener); - return Ok(()); - } - - let node = { - let mut write_guard = self.nodes.write().await; - let node = write_guard - .entry(listener.clone()) - .or_insert_with(|| Node::new(listener.clone())); - node.set_contact(username, display_name, url, avatar); - node.clone() - }; - self.save(listener, &node).await?; - Ok(()) - } - - pub async fn save(&self, listener: &Url, node: &Node) -> Result<(), MyError> { - let row_opt = self - .db - .pool() - .get() - .await? - .query_opt( - "SELECT id FROM listeners WHERE actor_id = $1::TEXT LIMIT 1;", - &[&listener.as_str()], - ) - .await?; - - let id: Uuid = if let Some(row) = row_opt { - row.try_get(0)? - } else { - return Err(MyError::NotSubscribed(listener.as_str().to_owned())); - }; - self.db - .pool() - .get() - .await? - .execute( - "INSERT INTO nodes ( - listener_id, - nodeinfo, - instance, - contact, - created_at, - updated_at - ) VALUES ( - $1::UUID, - $2::JSONB, - $3::JSONB, - $4::JSONB, - 'now', - 'now' - ) ON CONFLICT (listener_id) - DO UPDATE SET - nodeinfo = $2::JSONB, - instance = $3::JSONB, - contact = $4::JSONB;", - &[ - &id, - &Json(&node.info), - &Json(&node.instance), - &Json(&node.contact), - ], + .save_contact( + actor_id, + Contact { + username, + display_name, + url, + avatar, + updated: SystemTime::now(), + }, ) - .await?; - Ok(()) + .await } } -#[derive(Clone, Debug)] -pub struct Node { - pub base: Url, - pub info: Option, - pub instance: Option, - pub contact: Option, -} - impl Node { - pub fn new(mut url: Url) -> Self { + fn new(mut url: Url) -> Self { url.set_fragment(None); url.set_query(None); url.set_path(""); @@ -348,96 +153,38 @@ impl Node { } } - fn set_info(&mut self, software: String, version: String, reg: bool) -> &mut Self { - self.info = Some(Info { - software, - version, - reg, - updated: SystemTime::now(), - }); + fn info(mut self, info: Option) -> Self { + self.info = info; self } - fn set_instance( - &mut self, - title: String, - description: String, - version: String, - reg: bool, - requires_approval: bool, - ) -> &mut Self { - self.instance = Some(Instance { - title, - description, - version, - reg, - requires_approval, - updated: SystemTime::now(), - }); + fn instance(mut self, instance: Option) -> Self { + self.instance = instance; self } - fn set_contact( - &mut self, - username: String, - display_name: String, - url: Url, - avatar: Url, - ) -> &mut Self { - self.contact = Some(Contact { - username, - display_name, - url: url.into(), - avatar: avatar.into(), - updated: SystemTime::now(), - }); + fn contact(mut self, contact: Option) -> Self { + self.contact = contact; self } } -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -pub struct Info { - pub software: String, - pub version: String, - pub reg: bool, - pub updated: SystemTime, -} - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -pub struct Instance { - pub title: String, - pub description: String, - pub version: String, - pub reg: bool, - pub requires_approval: bool, - pub updated: SystemTime, -} - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -pub struct Contact { - pub username: String, - pub display_name: String, - pub url: Url, - pub avatar: Url, - pub updated: SystemTime, -} - static TEN_MINUTES: Duration = Duration::from_secs(60 * 10); impl Info { - pub fn outdated(&self) -> bool { + pub(crate) fn outdated(&self) -> bool { self.updated + TEN_MINUTES < SystemTime::now() } } impl Instance { - pub fn outdated(&self) -> bool { + pub(crate) fn outdated(&self) -> bool { self.updated + TEN_MINUTES < SystemTime::now() } } impl Contact { - pub fn outdated(&self) -> bool { + pub(crate) fn outdated(&self) -> bool { self.updated + TEN_MINUTES < SystemTime::now() } } diff --git a/src/data/state.rs b/src/data/state.rs index bc09eac..87b4938 100644 --- a/src/data/state.rs +++ b/src/data/state.rs @@ -6,38 +6,31 @@ use crate::{ requests::{Breakers, Requests}, }; use activitystreams::url::Url; -use actix_rt::{ - spawn, - time::{interval_at, Instant}, -}; use actix_web::web; use async_rwlock::RwLock; -use futures::{join, try_join}; -use log::{error, info}; +use log::info; use lru::LruCache; use rand::thread_rng; use rsa::{RSAPrivateKey, RSAPublicKey}; -use std::{collections::HashSet, sync::Arc, time::Duration}; +use std::sync::Arc; #[derive(Clone)] pub struct State { - pub public_key: RSAPublicKey, + pub(crate) public_key: RSAPublicKey, private_key: RSAPrivateKey, config: Config, - actor_id_cache: Arc>>, - blocks: Arc>>, - whitelists: Arc>>, - listeners: Arc>>, + object_cache: Arc>>, node_cache: NodeCache, breakers: Breakers, + pub(crate) db: Db, } impl State { - pub fn node_cache(&self) -> NodeCache { + pub(crate) fn node_cache(&self) -> NodeCache { self.node_cache.clone() } - pub fn requests(&self) -> Requests { + pub(crate) fn requests(&self) -> Requests { Requests::new( self.config.generate_url(UrlKind::MainKey).to_string(), self.private_key.clone(), @@ -51,168 +44,64 @@ impl State { ) } - pub async fn bust_whitelist(&self, whitelist: &str) { - self.whitelists.write().await.remove(whitelist); - } - - pub async fn bust_block(&self, block: &str) { - self.blocks.write().await.remove(block); - } - - pub async fn bust_listener(&self, inbox: &Url) { - self.listeners.write().await.remove(inbox); - } - - pub async fn listeners(&self) -> Vec { - self.listeners.read().await.iter().cloned().collect() - } - - pub async fn blocks(&self) -> Vec { - self.blocks.read().await.iter().cloned().collect() - } - - pub async fn listeners_without(&self, inbox: &Url, domain: &str) -> Vec { - self.listeners - .read() - .await + pub(crate) async fn inboxes_without( + &self, + existing_inbox: &Url, + domain: &str, + ) -> Result, MyError> { + Ok(self + .db + .inboxes() + .await? .iter() - .filter_map(|listener| { - if let Some(dom) = listener.domain() { - if listener != inbox && dom != domain { - return Some(listener.clone()); + .filter_map(|inbox| { + if let Some(dom) = inbox.domain() { + if inbox != existing_inbox && dom != domain { + return Some(inbox.clone()); } } None }) - .collect() + .collect()) } - pub async fn is_whitelisted(&self, actor_id: &Url) -> bool { - if !self.config.whitelist_mode() { - return true; - } - - if let Some(domain) = actor_id.domain() { - return self.whitelists.read().await.contains(domain); - } - - false + pub(crate) async fn is_cached(&self, object_id: &Url) -> bool { + self.object_cache.read().await.contains(object_id) } - pub async fn is_blocked(&self, actor_id: &Url) -> bool { - if let Some(domain) = actor_id.domain() { - return self.blocks.read().await.contains(domain); - } - - true + pub(crate) async fn cache(&self, object_id: Url, actor_id: Url) { + self.object_cache.write().await.put(object_id, actor_id); } - pub async fn is_listener(&self, actor_id: &Url) -> bool { - self.listeners.read().await.contains(actor_id) - } + pub(crate) async fn build(config: Config, db: Db) -> Result { + let private_key = if let Ok(Some(key)) = db.private_key().await { + key + } else { + info!("Generating new keys"); + let key = web::block(move || { + let mut rng = thread_rng(); + RSAPrivateKey::new(&mut rng, 4096) + }) + .await?; - pub async fn is_cached(&self, object_id: &Url) -> bool { - self.actor_id_cache.read().await.contains(object_id) - } + db.update_private_key(&key).await?; - pub async fn cache(&self, object_id: Url, actor_id: Url) { - self.actor_id_cache.write().await.put(object_id, actor_id); - } - - pub async fn cache_block(&self, host: String) { - self.blocks.write().await.insert(host); - } - - pub async fn cache_whitelist(&self, host: String) { - self.whitelists.write().await.insert(host); - } - - pub async fn cache_listener(&self, listener: Url) { - self.listeners.write().await.insert(listener); - } - - pub async fn rehydrate(&self, db: &Db) -> Result<(), MyError> { - let f1 = db.hydrate_blocks(); - let f2 = db.hydrate_whitelists(); - let f3 = db.hydrate_listeners(); - - let (blocks, whitelists, listeners) = try_join!(f1, f2, f3)?; - - join!( - async move { - *self.listeners.write().await = listeners; - }, - async move { - *self.whitelists.write().await = whitelists; - }, - async move { - *self.blocks.write().await = blocks; - } - ); - - Ok(()) - } - - pub async fn hydrate(config: Config, db: &Db) -> Result { - let f1 = db.hydrate_blocks(); - let f2 = db.hydrate_whitelists(); - let f3 = db.hydrate_listeners(); - - let f4 = async move { - if let Ok(Some(key)) = db.hydrate_private_key().await { - Ok(key) - } else { - info!("Generating new keys"); - let key = web::block(move || { - let mut rng = thread_rng(); - RSAPrivateKey::new(&mut rng, 4096) - }) - .await?; - - db.update_private_key(&key).await?; - - Ok(key) - } + key }; - let (blocks, whitelists, listeners, private_key) = try_join!(f1, f2, f3, f4)?; - let public_key = private_key.to_public_key(); - let listeners = Arc::new(RwLock::new(listeners)); let state = State { public_key, private_key, config, - actor_id_cache: Arc::new(RwLock::new(LruCache::new(1024 * 8))), - blocks: Arc::new(RwLock::new(blocks)), - whitelists: Arc::new(RwLock::new(whitelists)), - listeners: listeners.clone(), - node_cache: NodeCache::new(db.clone(), listeners), + object_cache: Arc::new(RwLock::new(LruCache::new(1024 * 8))), + node_cache: NodeCache::new(db.clone()), breakers: Breakers::default(), + db, }; - state.spawn_rehydrate(db.clone()); - Ok(state) } - - fn spawn_rehydrate(&self, db: Db) { - let state = self.clone(); - spawn(async move { - let start = Instant::now(); - let duration = Duration::from_secs(60 * 10); - - let mut interval = interval_at(start, duration); - - loop { - interval.tick().await; - - if let Err(e) = state.rehydrate(&db).await { - error!("Error rehydrating, {}", e); - } - } - }); - } } diff --git a/src/db.rs b/src/db.rs index 2d866ea..71d4851 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,294 +1,619 @@ -use crate::error::MyError; +use crate::{config::Config, error::MyError}; use activitystreams::url::Url; -use deadpool_postgres::{Manager, Pool}; -use log::{info, warn}; +use actix_web::web::Bytes; use rsa::RSAPrivateKey; use rsa_pem::KeyExt; -use std::collections::HashSet; -use tokio_postgres::{ - error::{Error, SqlState}, - row::Row, - Client, Config, NoTls, -}; +use sled::Tree; +use std::{collections::HashMap, sync::Arc, time::SystemTime}; +use uuid::Uuid; #[derive(Clone)] pub struct Db { - pool: Pool, + inner: Arc, +} + +struct Inner { + actor_id_actor: Tree, + public_key_id_actor_id: Tree, + connected_actor_ids: Tree, + allowed_domains: Tree, + blocked_domains: Tree, + settings: Tree, + media_url_media_id: Tree, + media_id_media_url: Tree, + media_id_media_bytes: Tree, + media_id_media_meta: Tree, + actor_id_info: Tree, + actor_id_instance: Tree, + actor_id_contact: Tree, + restricted_mode: bool, +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct Actor { + pub(crate) id: Url, + pub(crate) public_key: String, + pub(crate) public_key_id: Url, + pub(crate) inbox: Url, + pub(crate) saved_at: SystemTime, +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct MediaMeta { + pub(crate) media_type: String, + pub(crate) saved_at: SystemTime, +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct Info { + pub(crate) software: String, + pub(crate) version: String, + pub(crate) reg: bool, + pub(crate) updated: SystemTime, +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct Instance { + pub(crate) title: String, + pub(crate) description: String, + pub(crate) version: String, + pub(crate) reg: bool, + pub(crate) requires_approval: bool, + pub(crate) updated: SystemTime, +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct Contact { + pub(crate) username: String, + pub(crate) display_name: String, + pub(crate) url: Url, + pub(crate) avatar: Url, + pub(crate) updated: SystemTime, +} + +impl Inner { + fn connected_by_domain(&self, domains: &[String]) -> impl DoubleEndedIterator { + let reversed: Vec<_> = domains + .into_iter() + .map(|s| domain_key(s.as_str())) + .collect(); + + self.connected_actor_ids + .iter() + .values() + .filter_map(|res| res.ok()) + .filter_map(url_from_ivec) + .filter_map(move |url| { + let connected_domain = url.domain()?; + let connected_rdnn = domain_key(connected_domain); + + for rdnn in &reversed { + if connected_rdnn.starts_with(rdnn) { + return Some(url); + } + } + + None + }) + } + + fn blocks(&self) -> impl DoubleEndedIterator { + self.blocked_domains + .iter() + .values() + .filter_map(|res| res.ok()) + .map(|s| String::from_utf8_lossy(&s).to_string()) + } + + fn connected(&self) -> impl DoubleEndedIterator { + self.connected_actor_ids + .iter() + .values() + .filter_map(|res| res.ok()) + .filter_map(url_from_ivec) + } + + fn connected_actors<'a>(&'a self) -> impl DoubleEndedIterator + 'a { + self.connected_actor_ids + .iter() + .values() + .filter_map(|res| res.ok()) + .filter_map(move |actor_id| { + let actor_ivec = self.actor_id_actor.get(actor_id).ok()??; + + serde_json::from_slice::(&actor_ivec).ok() + }) + } + + fn connected_info<'a>(&'a self) -> impl DoubleEndedIterator + 'a { + self.connected_actor_ids + .iter() + .values() + .filter_map(|res| res.ok()) + .filter_map(move |actor_id_ivec| { + let actor_id = url_from_ivec(actor_id_ivec.clone())?; + let ivec = self.actor_id_info.get(actor_id_ivec).ok()??; + let info = serde_json::from_slice(&ivec).ok()?; + + Some((actor_id, info)) + }) + } + + fn connected_instance<'a>(&'a self) -> impl DoubleEndedIterator + 'a { + self.connected_actor_ids + .iter() + .values() + .filter_map(|res| res.ok()) + .filter_map(move |actor_id_ivec| { + let actor_id = url_from_ivec(actor_id_ivec.clone())?; + let ivec = self.actor_id_instance.get(actor_id_ivec).ok()??; + let instance = serde_json::from_slice(&ivec).ok()?; + + Some((actor_id, instance)) + }) + } + + fn connected_contact<'a>(&'a self) -> impl DoubleEndedIterator + 'a { + self.connected_actor_ids + .iter() + .values() + .filter_map(|res| res.ok()) + .filter_map(move |actor_id_ivec| { + let actor_id = url_from_ivec(actor_id_ivec.clone())?; + let ivec = self.actor_id_contact.get(actor_id_ivec).ok()??; + let contact = serde_json::from_slice(&ivec).ok()?; + + Some((actor_id, contact)) + }) + } + + fn is_allowed(&self, domain: &str) -> bool { + let prefix = domain_prefix(domain); + + if self.restricted_mode { + self.allowed_domains + .scan_prefix(prefix) + .keys() + .filter_map(|res| res.ok()) + .any(|rdnn| domain.starts_with(String::from_utf8_lossy(&rdnn).as_ref())) + } else { + !self + .blocked_domains + .scan_prefix(prefix) + .keys() + .filter_map(|res| res.ok()) + .any(|rdnn| domain.starts_with(String::from_utf8_lossy(&rdnn).as_ref())) + } + } } impl Db { - pub fn build(config: &crate::config::Config) -> Result { - let max_conns = config.max_connections(); - let config: Config = config.database_url().parse()?; - - let manager = Manager::new(config, NoTls); + pub(crate) fn build(config: &Config) -> Result { + let db = sled::open(config.sled_path())?; + let restricted_mode = config.restricted_mode(); Ok(Db { - pool: Pool::new(manager, max_conns), + inner: Arc::new(Inner { + actor_id_actor: db.open_tree("actor-id-actor")?, + public_key_id_actor_id: db.open_tree("public-key-id-actor-id")?, + connected_actor_ids: db.open_tree("connected-actor-ids")?, + allowed_domains: db.open_tree("allowed-actor-ids")?, + blocked_domains: db.open_tree("blocked-actor-ids")?, + settings: db.open_tree("settings")?, + media_url_media_id: db.open_tree("media-url-media-id")?, + media_id_media_url: db.open_tree("media-id-media-url")?, + media_id_media_bytes: db.open_tree("media-id-media-bytes")?, + media_id_media_meta: db.open_tree("media-id-media-meta")?, + actor_id_info: db.open_tree("actor-id-info")?, + actor_id_instance: db.open_tree("actor-id-instance")?, + actor_id_contact: db.open_tree("actor-id-contact")?, + restricted_mode, + }), }) } - pub fn pool(&self) -> &Pool { - &self.pool + async fn unblock( + &self, + f: impl Fn(&Inner) -> Result + Send + 'static, + ) -> Result + where + T: Send + 'static, + { + let inner = self.inner.clone(); + + let t = actix_web::web::block(move || (f)(&inner)).await?; + + Ok(t) } - pub async fn remove_listener(&self, inbox: Url) -> Result<(), MyError> { - info!("DELETE FROM listeners WHERE actor_id = {};", inbox.as_str()); - self.pool - .get() - .await? - .execute( - "DELETE FROM listeners WHERE actor_id = $1::TEXT;", - &[&inbox.as_str()], - ) - .await?; - - Ok(()) + pub(crate) async fn connected_ids(&self) -> Result, MyError> { + self.unblock(|inner| Ok(inner.connected().collect())).await } - pub async fn add_listener(&self, inbox: Url) -> Result<(), MyError> { - info!( - "INSERT INTO listeners (actor_id, created_at) VALUES ($1::TEXT, 'now'); [{}]", - inbox.as_str(), - ); - self.pool - .get() - .await? - .execute( - "INSERT INTO listeners (actor_id, created_at) VALUES ($1::TEXT, 'now');", - &[&inbox.as_str()], - ) - .await?; + pub(crate) async fn save_info(&self, actor_id: Url, info: Info) -> Result<(), MyError> { + self.unblock(move |inner| { + let vec = serde_json::to_vec(&info)?; - Ok(()) + inner + .actor_id_info + .insert(actor_id.as_str().as_bytes(), vec)?; + + Ok(()) + }) + .await } - pub async fn add_blocks(&self, domains: &[String]) -> Result<(), MyError> { - let conn = self.pool.get().await?; - for domain in domains { - match add_block(&conn, domain.as_str()).await { - Err(e) if e.code() != Some(&SqlState::UNIQUE_VIOLATION) => { - return Err(e.into()); - } - _ => (), - }; - } - Ok(()) - } - - pub async fn remove_blocks(&self, domains: &[String]) -> Result<(), MyError> { - let conn = self.pool.get().await?; - for domain in domains { - remove_block(&conn, domain.as_str()).await? - } - Ok(()) - } - - pub async fn add_whitelists(&self, domains: &[String]) -> Result<(), MyError> { - let conn = self.pool.get().await?; - for domain in domains { - match add_whitelist(&conn, domain.as_str()).await { - Err(e) if e.code() != Some(&SqlState::UNIQUE_VIOLATION) => { - return Err(e.into()); - } - _ => (), - }; - } - Ok(()) - } - - pub async fn remove_whitelists(&self, domains: &[String]) -> Result<(), MyError> { - let conn = self.pool.get().await?; - for domain in domains { - remove_whitelist(&conn, domain.as_str()).await? - } - Ok(()) - } - - pub async fn hydrate_blocks(&self) -> Result, MyError> { - info!("SELECT domain_name FROM blocks"); - let rows = self - .pool - .get() - .await? - .query("SELECT domain_name FROM blocks", &[]) - .await?; - - parse_rows(rows) - } - - pub async fn hydrate_whitelists(&self) -> Result, MyError> { - info!("SELECT domain_name FROM whitelists"); - let rows = self - .pool - .get() - .await? - .query("SELECT domain_name FROM whitelists", &[]) - .await?; - - parse_rows(rows) - } - - pub async fn hydrate_listeners(&self) -> Result, MyError> { - info!("SELECT actor_id FROM listeners"); - let rows = self - .pool - .get() - .await? - .query("SELECT actor_id FROM listeners", &[]) - .await?; - - parse_rows(rows) - } - - pub async fn hydrate_private_key(&self) -> Result, MyError> { - info!("SELECT value FROM settings WHERE key = 'private_key'"); - let rows = self - .pool - .get() - .await? - .query("SELECT value FROM settings WHERE key = 'private_key'", &[]) - .await?; - - if let Some(row) = rows.into_iter().next() { - let key_str: String = row.get(0); - // precomputation happens when constructing a private key, so it should be on the - // threadpool - let key = actix_web::web::block(move || KeyExt::from_pem_pkcs8(&key_str)).await?; - - return Ok(Some(key)); - } - - Ok(None) - } - - pub async fn update_private_key(&self, private_key: &RSAPrivateKey) -> Result<(), MyError> { - let pem_pkcs8 = private_key.to_pem_pkcs8()?; - - info!( - "INSERT INTO settings (key, value, created_at) - VALUES ('private_key', $1::TEXT, 'now') - ON CONFLICT (key) - DO UPDATE - SET value = $1::TEXT;" - ); - self.pool - .get() - .await? - .execute( - "INSERT INTO settings (key, value, created_at) - VALUES ('private_key', $1::TEXT, 'now') - ON CONFLICT (key) - DO UPDATE - SET value = $1::TEXT;", - &[&pem_pkcs8], - ) - .await?; - Ok(()) - } -} - -pub async fn listen(client: &Client) -> Result<(), Error> { - info!("LISTEN new_blocks, new_whitelists, new_listeners, new_actors, rm_blocks, rm_whitelists, rm_listeners, rm_actors"); - client - .batch_execute( - "LISTEN new_blocks; - LISTEN new_whitelists; - LISTEN new_listeners; - LISTEN new_actors; - LISTEN new_nodes; - LISTEN rm_blocks; - LISTEN rm_whitelists; - LISTEN rm_listeners; - LISTEN rm_actors; - LISTEN rm_nodes", - ) - .await?; - - Ok(()) -} - -async fn add_block(client: &Client, domain: &str) -> Result<(), Error> { - info!( - "INSERT INTO blocks (domain_name, created_at) VALUES ($1::TEXT, 'now'); [{}]", - domain, - ); - client - .execute( - "INSERT INTO blocks (domain_name, created_at) VALUES ($1::TEXT, 'now');", - &[&domain], - ) - .await?; - - Ok(()) -} - -async fn remove_block(client: &Client, domain: &str) -> Result<(), Error> { - info!( - "DELETE FROM blocks WHERE domain_name = $1::TEXT; [{}]", - domain, - ); - client - .execute( - "DELETE FROM blocks WHERE domain_name = $1::TEXT;", - &[&domain], - ) - .await?; - - Ok(()) -} - -async fn add_whitelist(client: &Client, domain: &str) -> Result<(), Error> { - info!( - "INSERT INTO whitelists (domain_name, created_at) VALUES ($1::TEXT, 'now'); [{}]", - domain, - ); - client - .execute( - "INSERT INTO whitelists (domain_name, created_at) VALUES ($1::TEXT, 'now');", - &[&domain], - ) - .await?; - - Ok(()) -} - -async fn remove_whitelist(client: &Client, domain: &str) -> Result<(), Error> { - info!( - "DELETE FROM whitelists WHERE domain_name = $1::TEXT; [{}]", - domain, - ); - client - .execute( - "DELETE FROM whitelists WHERE domain_name = $1::TEXT;", - &[&domain], - ) - .await?; - - Ok(()) -} - -fn parse_rows(rows: Vec) -> Result, MyError> -where - T: std::str::FromStr + Eq + std::hash::Hash, - E: std::fmt::Display, -{ - let hs = rows - .into_iter() - .filter_map(move |row| match row.try_get::<_, String>(0) { - Ok(s) => match s.parse() { - Ok(t) => Some(t), - Err(e) => { - warn!("Couln't parse row, '{}', {}", s, e); - None - } - }, - Err(e) => { - warn!("Couldn't get column, {}", e); - None + pub(crate) async fn info(&self, actor_id: Url) -> Result, MyError> { + self.unblock(move |inner| { + if let Some(ivec) = inner.actor_id_info.get(actor_id.as_str().as_bytes())? { + let info = serde_json::from_slice(&ivec)?; + Ok(Some(info)) + } else { + Ok(None) } }) - .collect(); + .await + } - Ok(hs) + pub(crate) async fn connected_info(&self) -> Result, MyError> { + self.unblock(|inner| Ok(inner.connected_info().collect())) + .await + } + + pub(crate) async fn save_instance( + &self, + actor_id: Url, + instance: Instance, + ) -> Result<(), MyError> { + self.unblock(move |inner| { + let vec = serde_json::to_vec(&instance)?; + + inner + .actor_id_instance + .insert(actor_id.as_str().as_bytes(), vec)?; + + Ok(()) + }) + .await + } + + pub(crate) async fn instance(&self, actor_id: Url) -> Result, MyError> { + self.unblock(move |inner| { + if let Some(ivec) = inner.actor_id_instance.get(actor_id.as_str().as_bytes())? { + let instance = serde_json::from_slice(&ivec)?; + Ok(Some(instance)) + } else { + Ok(None) + } + }) + .await + } + + pub(crate) async fn connected_instance(&self) -> Result, MyError> { + self.unblock(|inner| Ok(inner.connected_instance().collect())) + .await + } + + pub(crate) async fn save_contact( + &self, + actor_id: Url, + contact: Contact, + ) -> Result<(), MyError> { + self.unblock(move |inner| { + let vec = serde_json::to_vec(&contact)?; + + inner + .actor_id_contact + .insert(actor_id.as_str().as_bytes(), vec)?; + + Ok(()) + }) + .await + } + + pub(crate) async fn contact(&self, actor_id: Url) -> Result, MyError> { + self.unblock(move |inner| { + if let Some(ivec) = inner.actor_id_contact.get(actor_id.as_str().as_bytes())? { + let contact = serde_json::from_slice(&ivec)?; + Ok(Some(contact)) + } else { + Ok(None) + } + }) + .await + } + + pub(crate) async fn connected_contact(&self) -> Result, MyError> { + self.unblock(|inner| Ok(inner.connected_contact().collect())) + .await + } + + pub(crate) async fn save_url(&self, url: Url, id: Uuid) -> Result<(), MyError> { + self.unblock(move |inner| { + inner + .media_id_media_url + .insert(id.as_bytes(), url.as_str().as_bytes())?; + inner + .media_url_media_id + .insert(url.as_str().as_bytes(), id.as_bytes())?; + Ok(()) + }) + .await + } + + pub(crate) async fn save_bytes( + &self, + id: Uuid, + meta: MediaMeta, + bytes: Bytes, + ) -> Result<(), MyError> { + self.unblock(move |inner| { + let vec = serde_json::to_vec(&meta)?; + + inner + .media_id_media_bytes + .insert(id.as_bytes(), bytes.as_ref())?; + inner.media_id_media_meta.insert(id.as_bytes(), vec)?; + + Ok(()) + }) + .await + } + + pub(crate) async fn media_id(&self, url: Url) -> Result, MyError> { + self.unblock(move |inner| { + if let Some(ivec) = inner.media_url_media_id.get(url.as_str().as_bytes())? { + Ok(uuid_from_ivec(ivec)) + } else { + Ok(None) + } + }) + .await + } + + pub(crate) async fn media_url(&self, id: Uuid) -> Result, MyError> { + self.unblock(move |inner| { + if let Some(ivec) = inner.media_id_media_url.get(id.as_bytes())? { + Ok(url_from_ivec(ivec)) + } else { + Ok(None) + } + }) + .await + } + + pub(crate) async fn media_bytes(&self, id: Uuid) -> Result, MyError> { + self.unblock(move |inner| { + if let Some(ivec) = inner.media_id_media_bytes.get(id.as_bytes())? { + Ok(Some(Bytes::copy_from_slice(&ivec))) + } else { + Ok(None) + } + }) + .await + } + + pub(crate) async fn media_meta(&self, id: Uuid) -> Result, MyError> { + self.unblock(move |inner| { + if let Some(ivec) = inner.media_id_media_meta.get(id.as_bytes())? { + let meta = serde_json::from_slice(&ivec)?; + Ok(Some(meta)) + } else { + Ok(None) + } + }) + .await + } + + pub(crate) async fn blocks(&self) -> Result, MyError> { + self.unblock(|inner| Ok(inner.blocks().collect())).await + } + + pub(crate) async fn inboxes(&self) -> Result, MyError> { + self.unblock(|inner| Ok(inner.connected_actors().map(|actor| actor.inbox).collect())) + .await + } + + pub(crate) async fn is_connected(&self, id: Url) -> Result { + self.unblock(move |inner| { + let connected = inner + .connected_actor_ids + .contains_key(id.as_str().as_bytes())?; + + Ok(connected) + }) + .await + } + + pub(crate) async fn actor_id_from_public_key_id( + &self, + public_key_id: Url, + ) -> Result, MyError> { + self.unblock(move |inner| { + if let Some(ivec) = inner + .public_key_id_actor_id + .get(public_key_id.as_str().as_bytes())? + { + Ok(url_from_ivec(ivec)) + } else { + Ok(None) + } + }) + .await + } + + pub(crate) async fn actor(&self, actor_id: Url) -> Result, MyError> { + self.unblock(move |inner| { + if let Some(ivec) = inner.actor_id_actor.get(actor_id.as_str().as_bytes())? { + let actor = serde_json::from_slice(&ivec)?; + Ok(Some(actor)) + } else { + Ok(None) + } + }) + .await + } + + pub(crate) async fn save_actor(&self, actor: Actor) -> Result<(), MyError> { + self.unblock(move |inner| { + let vec = serde_json::to_vec(&actor)?; + + inner.public_key_id_actor_id.insert( + actor.public_key_id.as_str().as_bytes(), + actor.id.as_str().as_bytes(), + )?; + inner + .actor_id_actor + .insert(actor.id.as_str().as_bytes(), vec)?; + Ok(()) + }) + .await + } + + pub(crate) async fn remove_listener(&self, actor_id: Url) -> Result<(), MyError> { + self.unblock(move |inner| { + inner + .connected_actor_ids + .remove(actor_id.as_str().as_bytes())?; + + Ok(()) + }) + .await + } + + pub(crate) async fn add_listener(&self, actor_id: Url) -> Result<(), MyError> { + self.unblock(move |inner| { + inner + .connected_actor_ids + .insert(actor_id.as_str().as_bytes(), actor_id.as_str().as_bytes())?; + + Ok(()) + }) + .await + } + + pub(crate) async fn add_blocks(&self, domains: Vec) -> Result<(), MyError> { + self.unblock(move |inner| { + for connected in inner.connected_by_domain(&domains) { + inner + .connected_actor_ids + .remove(connected.as_str().as_bytes())?; + } + + for domain in &domains { + inner + .blocked_domains + .insert(domain_key(domain), domain.as_bytes())?; + inner.allowed_domains.remove(domain_key(domain))?; + } + + Ok(()) + }) + .await + } + + pub(crate) async fn remove_blocks(&self, domains: Vec) -> Result<(), MyError> { + self.unblock(move |inner| { + for domain in &domains { + inner.blocked_domains.remove(domain_key(domain))?; + } + + Ok(()) + }) + .await + } + + pub(crate) async fn add_allows(&self, domains: Vec) -> Result<(), MyError> { + self.unblock(move |inner| { + for domain in &domains { + inner + .allowed_domains + .insert(domain_key(domain), domain.as_bytes())?; + } + + Ok(()) + }) + .await + } + + pub(crate) async fn remove_allows(&self, domains: Vec) -> Result<(), MyError> { + self.unblock(move |inner| { + if inner.restricted_mode { + for connected in inner.connected_by_domain(&domains) { + inner + .connected_actor_ids + .remove(connected.as_str().as_bytes())?; + } + } + + for domain in &domains { + inner.allowed_domains.remove(domain_key(domain))?; + } + + Ok(()) + }) + .await + } + + pub(crate) async fn is_allowed(&self, url: Url) -> Result { + self.unblock(move |inner| { + if let Some(domain) = url.domain() { + Ok(inner.is_allowed(domain)) + } else { + Ok(false) + } + }) + .await + } + + pub(crate) async fn private_key(&self) -> Result, MyError> { + self.unblock(|inner| { + if let Some(ivec) = inner.settings.get("private-key")? { + let key_str = String::from_utf8_lossy(&ivec); + let key = RSAPrivateKey::from_pem_pkcs8(&key_str)?; + + Ok(Some(key)) + } else { + Ok(None) + } + }) + .await + } + + pub(crate) async fn update_private_key( + &self, + private_key: &RSAPrivateKey, + ) -> Result<(), MyError> { + let pem_pkcs8 = private_key.to_pem_pkcs8()?; + + self.unblock(move |inner| { + inner + .settings + .insert("private-key".as_bytes(), pem_pkcs8.as_bytes())?; + Ok(()) + }) + .await + } +} + +fn domain_key(domain: &str) -> String { + domain.split('.').rev().collect::>().join(".") + "." +} + +fn domain_prefix(domain: &str) -> String { + domain + .split('.') + .rev() + .take(2) + .collect::>() + .join(".") + + "." +} + +fn url_from_ivec(ivec: sled::IVec) -> Option { + String::from_utf8_lossy(&ivec).parse::().ok() +} + +fn uuid_from_ivec(ivec: sled::IVec) -> Option { + Uuid::from_slice(&ivec).ok() } diff --git a/src/error.rs b/src/error.rs index f9a9d39..9670b87 100644 --- a/src/error.rs +++ b/src/error.rs @@ -4,7 +4,6 @@ use actix_web::{ http::StatusCode, HttpResponse, }; -use deadpool::managed::{PoolError, TimeoutType}; use http_signature_normalization_actix::PrepareSignError; use log::error; use rsa_pem::KeyError; @@ -18,9 +17,6 @@ pub enum MyError { #[error("Error in configuration, {0}")] Config(#[from] config::ConfigError), - #[error("Error in db, {0}")] - DbError(#[from] tokio_postgres::error::Error), - #[error("Couldn't parse key, {0}")] Key(#[from] KeyError), @@ -33,6 +29,9 @@ pub enum MyError { #[error("Couldn't sign string, {0}")] Rsa(rsa::errors::Error), + #[error("Couldn't use db, {0}")] + Sled(#[from] sled::Error), + #[error("Couldn't do the json thing, {0}")] Json(#[from] serde_json::Error), @@ -48,11 +47,8 @@ pub enum MyError { #[error("Actor ({0}), or Actor's server, is not subscribed")] NotSubscribed(String), - #[error("Actor is blocked, {0}")] - Blocked(String), - - #[error("Actor is not whitelisted, {0}")] - Whitelist(String), + #[error("Actor is not allowed, {0}")] + NotAllowed(String), #[error("Cannot make decisions for foreign actor, {0}")] WrongActor(String), @@ -78,9 +74,6 @@ pub enum MyError { #[error("Couldn't flush buffer")] FlushBuffer, - #[error("Timed out while waiting on db pool, {0:?}")] - DbTimeout(TimeoutType), - #[error("Invalid algorithm provided to verifier, {0}")] Algorithm(String), @@ -127,10 +120,9 @@ pub enum MyError { impl ResponseError for MyError { fn status_code(&self) -> StatusCode { match self { - MyError::Blocked(_) - | MyError::Whitelist(_) - | MyError::WrongActor(_) - | MyError::BadActor(_, _) => StatusCode::FORBIDDEN, + MyError::NotAllowed(_) | MyError::WrongActor(_) | MyError::BadActor(_, _) => { + StatusCode::FORBIDDEN + } MyError::NotSubscribed(_) => StatusCode::UNAUTHORIZED, MyError::Duplicate => StatusCode::ACCEPTED, MyError::Kind(_) | MyError::MissingKind | MyError::MissingId | MyError::ObjectCount => { @@ -161,18 +153,6 @@ where } } -impl From> for MyError -where - T: Into, -{ - fn from(e: PoolError) -> Self { - match e { - PoolError::Backend(e) => e.into(), - PoolError::Timeout(t) => MyError::DbTimeout(t), - } - } -} - impl From for MyError { fn from(i: Infallible) -> Self { match i {} diff --git a/src/jobs/apub/announce.rs b/src/jobs/apub/announce.rs index dc19bbe..8c4e9f9 100644 --- a/src/jobs/apub/announce.rs +++ b/src/jobs/apub/announce.rs @@ -1,6 +1,6 @@ use crate::{ config::{Config, UrlKind}, - data::Actor, + db::Actor, error::MyError, jobs::{ apub::{get_inboxes, prepare_activity}, diff --git a/src/jobs/apub/follow.rs b/src/jobs/apub/follow.rs index 8af768b..352d0c9 100644 --- a/src/jobs/apub/follow.rs +++ b/src/jobs/apub/follow.rs @@ -1,7 +1,7 @@ use crate::{ apub::AcceptedActivities, config::{Config, UrlKind}, - data::Actor, + db::Actor, error::MyError, jobs::{apub::prepare_activity, Deliver, JobState}, }; @@ -36,14 +36,16 @@ impl Follow { let my_id = state.config.generate_url(UrlKind::Actor); // if following relay directly, not just following 'public', followback - if self.input.object_is(&my_id) && !state.actors.is_following(&self.actor.id).await { + if self.input.object_is(&my_id) + && !state.state.db.is_connected(self.actor.id.clone()).await? + { let follow = generate_follow(&state.config, &self.actor.id, &my_id)?; state .job_server .queue(Deliver::new(self.actor.inbox.clone(), follow)?)?; } - state.actors.follower(&self.actor).await?; + state.actors.follower(self.actor.clone()).await?; let accept = generate_accept_follow( &state.config, diff --git a/src/jobs/apub/forward.rs b/src/jobs/apub/forward.rs index a8e4c29..36faea1 100644 --- a/src/jobs/apub/forward.rs +++ b/src/jobs/apub/forward.rs @@ -1,6 +1,6 @@ use crate::{ apub::AcceptedActivities, - data::Actor, + db::Actor, error::MyError, jobs::{apub::get_inboxes, DeliverMany, JobState}, }; diff --git a/src/jobs/apub/mod.rs b/src/jobs/apub/mod.rs index 9829cb2..9cd40c9 100644 --- a/src/jobs/apub/mod.rs +++ b/src/jobs/apub/mod.rs @@ -1,6 +1,7 @@ use crate::{ config::{Config, UrlKind}, - data::{Actor, State}, + data::State, + db::Actor, error::MyError, }; use activitystreams::{ @@ -23,7 +24,7 @@ pub use self::{announce::Announce, follow::Follow, forward::Forward, reject::Rej async fn get_inboxes(state: &State, actor: &Actor, object_id: &Url) -> Result, MyError> { let domain = object_id.host().ok_or(MyError::Domain)?.to_string(); - Ok(state.listeners_without(&actor.inbox, &domain).await) + state.inboxes_without(&actor.inbox, &domain).await } fn prepare_activity( diff --git a/src/jobs/apub/reject.rs b/src/jobs/apub/reject.rs index 99f66bc..2d25d6e 100644 --- a/src/jobs/apub/reject.rs +++ b/src/jobs/apub/reject.rs @@ -1,6 +1,6 @@ use crate::{ config::UrlKind, - data::Actor, + db::Actor, jobs::{apub::generate_undo_follow, Deliver, JobState}, }; use background_jobs::ActixJob; @@ -11,9 +11,7 @@ pub struct Reject(pub Actor); impl Reject { async fn perform(self, state: JobState) -> Result<(), anyhow::Error> { - if state.actors.unfollower(&self.0).await?.is_some() { - state.db.remove_listener(self.0.inbox.clone()).await?; - } + state.actors.unfollower(&self.0).await?; let my_id = state.config.generate_url(UrlKind::Actor); let undo = generate_undo_follow(&state.config, &self.0.id, &my_id)?; diff --git a/src/jobs/apub/undo.rs b/src/jobs/apub/undo.rs index c8fc4b0..4262871 100644 --- a/src/jobs/apub/undo.rs +++ b/src/jobs/apub/undo.rs @@ -1,7 +1,7 @@ use crate::{ apub::AcceptedActivities, config::UrlKind, - data::Actor, + db::Actor, jobs::{apub::generate_undo_follow, Deliver, JobState}, }; use background_jobs::ActixJob; @@ -19,11 +19,9 @@ impl Undo { } async fn perform(self, state: JobState) -> Result<(), anyhow::Error> { - let was_following = state.actors.is_following(&self.actor.id).await; + let was_following = state.state.db.is_connected(self.actor.id.clone()).await?; - if state.actors.unfollower(&self.actor).await?.is_some() { - state.db.remove_listener(self.actor.inbox.clone()).await?; - } + state.actors.unfollower(&self.actor).await?; if was_following { let my_id = state.config.generate_url(UrlKind::Actor); diff --git a/src/jobs/cache_media.rs b/src/jobs/cache_media.rs index d8e7001..c1e5482 100644 --- a/src/jobs/cache_media.rs +++ b/src/jobs/cache_media.rs @@ -15,7 +15,7 @@ impl CacheMedia { } async fn perform(self, state: JobState) -> Result<(), Error> { - if state.media.get_bytes(self.uuid).await.is_some() { + if !state.media.is_outdated(self.uuid).await? { return Ok(()); } @@ -25,7 +25,7 @@ impl CacheMedia { state .media .store_bytes(self.uuid, content_type, bytes) - .await; + .await?; } Ok(()) diff --git a/src/jobs/instance.rs b/src/jobs/instance.rs index 08ebbaa..044f2bf 100644 --- a/src/jobs/instance.rs +++ b/src/jobs/instance.rs @@ -5,32 +5,35 @@ use crate::{ use activitystreams::url::Url; use anyhow::Error; use background_jobs::ActixJob; -use futures::join; use std::{future::Future, pin::Pin}; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] pub struct QueryInstance { - listener: Url, + actor_id: Url, } impl QueryInstance { - pub fn new(listener: Url) -> Self { + pub fn new(actor_id: Url) -> Self { QueryInstance { - listener: listener.into(), + actor_id: actor_id.into(), } } async fn perform(self, state: JobState) -> Result<(), Error> { - let (o1, o2) = join!( - state.node_cache.is_contact_outdated(&self.listener), - state.node_cache.is_instance_outdated(&self.listener), - ); + let contact_outdated = state + .node_cache + .is_contact_outdated(self.actor_id.clone()) + .await; + let instance_outdated = state + .node_cache + .is_instance_outdated(self.actor_id.clone()) + .await; - if !(o1 || o2) { + if !(contact_outdated || instance_outdated) { return Ok(()); } - let mut instance_uri = self.listener.clone(); + let mut instance_uri = self.actor_id.clone(); instance_uri.set_fragment(None); instance_uri.set_query(None); instance_uri.set_path("api/v1/instance"); @@ -47,11 +50,11 @@ impl QueryInstance { }; if let Some(mut contact) = instance.contact { - let uuid = if let Some(uuid) = state.media.get_uuid(&contact.avatar).await? { + let uuid = if let Some(uuid) = state.media.get_uuid(contact.avatar.clone()).await? { contact.avatar = state.config.generate_url(UrlKind::Media(uuid)).into(); uuid } else { - let uuid = state.media.store_url(&contact.avatar).await?; + let uuid = state.media.store_url(contact.avatar.clone()).await?; contact.avatar = state.config.generate_url(UrlKind::Media(uuid)).into(); uuid }; @@ -61,7 +64,7 @@ impl QueryInstance { state .node_cache .set_contact( - &self.listener, + self.actor_id.clone(), contact.username, contact.display_name, contact.url, @@ -75,7 +78,7 @@ impl QueryInstance { state .node_cache .set_instance( - &self.listener, + self.actor_id.clone(), instance.title, description, instance.version, diff --git a/src/jobs/mod.rs b/src/jobs/mod.rs index 73a96d7..1761b51 100644 --- a/src/jobs/mod.rs +++ b/src/jobs/mod.rs @@ -13,7 +13,7 @@ pub use self::{ use crate::{ config::Config, - data::{ActorCache, Media, NodeCache, State}, + data::{ActorCache, MediaCache, NodeCache, State}, db::Db, error::MyError, jobs::process_listeners::Listeners, @@ -35,7 +35,7 @@ pub fn create_workers( state: State, actors: ActorCache, job_server: JobServer, - media: Media, + media: MediaCache, config: Config, ) { let remote_handle = job_server.remote.clone(); @@ -72,7 +72,7 @@ pub struct JobState { state: State, actors: ActorCache, config: Config, - media: Media, + media: MediaCache, node_cache: NodeCache, job_server: JobServer, } @@ -88,7 +88,7 @@ impl JobState { state: State, actors: ActorCache, job_server: JobServer, - media: Media, + media: MediaCache, config: Config, ) -> Self { JobState { diff --git a/src/jobs/nodeinfo.rs b/src/jobs/nodeinfo.rs index 48b340d..2d0b952 100644 --- a/src/jobs/nodeinfo.rs +++ b/src/jobs/nodeinfo.rs @@ -6,20 +6,24 @@ use std::{future::Future, pin::Pin}; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] pub struct QueryNodeinfo { - listener: Url, + actor_id: Url, } impl QueryNodeinfo { - pub fn new(listener: Url) -> Self { - QueryNodeinfo { listener } + pub fn new(actor_id: Url) -> Self { + QueryNodeinfo { actor_id } } async fn perform(self, state: JobState) -> Result<(), Error> { - if !state.node_cache.is_nodeinfo_outdated(&self.listener).await { + if !state + .node_cache + .is_nodeinfo_outdated(self.actor_id.clone()) + .await + { return Ok(()); } - let mut well_known_uri = self.listener.clone(); + let mut well_known_uri = self.actor_id.clone(); well_known_uri.set_fragment(None); well_known_uri.set_query(None); well_known_uri.set_path(".well-known/nodeinfo"); @@ -40,7 +44,7 @@ impl QueryNodeinfo { state .node_cache .set_info( - &self.listener, + self.actor_id.clone(), nodeinfo.software.name, nodeinfo.software.version, nodeinfo.open_registrations, diff --git a/src/jobs/process_listeners.rs b/src/jobs/process_listeners.rs index b44fc1f..75ef255 100644 --- a/src/jobs/process_listeners.rs +++ b/src/jobs/process_listeners.rs @@ -8,11 +8,11 @@ pub struct Listeners; impl Listeners { async fn perform(self, state: JobState) -> Result<(), Error> { - for listener in state.state.listeners().await { + for actor_id in state.state.db.connected_ids().await? { state .job_server - .queue(QueryInstance::new(listener.clone()))?; - state.job_server.queue(QueryNodeinfo::new(listener))?; + .queue(QueryInstance::new(actor_id.clone()))?; + state.job_server.queue(QueryNodeinfo::new(actor_id))?; } Ok(()) diff --git a/src/main.rs b/src/main.rs index 8e6d2ff..1191671 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -use actix_rt::Arbiter; use actix_web::{ middleware::{Compress, Logger}, web, App, HttpServer, @@ -12,14 +11,13 @@ mod db; mod error; mod jobs; mod middleware; -mod notify; mod requests; mod routes; use self::{ args::Args, config::Config, - data::{ActorCache, Media, State}, + data::{ActorCache, MediaCache, State}, db::Db, jobs::{create_server, create_workers}, middleware::{DebugPayload, RelayResolver}, @@ -35,7 +33,7 @@ async fn main() -> Result<(), anyhow::Error> { if config.debug() { std::env::set_var( "RUST_LOG", - "debug,tokio_postgres=info,h2=info,trust_dns_resolver=info,trust_dns_proto=info,rustls=info,html5ever=info", + "debug,h2=info,trust_dns_resolver=info,trust_dns_proto=info,rustls=info,html5ever=info", ) } else { std::env::set_var("RUST_LOG", "info") @@ -51,73 +49,33 @@ async fn main() -> Result<(), anyhow::Error> { let args = Args::new(); - if args.jobs_only() && args.no_jobs() { - return Err(anyhow::Error::msg( - "Either the server or the jobs must be run", - )); - } - - if !args.blocks().is_empty() || !args.whitelists().is_empty() { + if !args.blocks().is_empty() || !args.allowed().is_empty() { if args.undo() { - db.remove_blocks(args.blocks()).await?; - db.remove_whitelists(args.whitelists()).await?; + db.remove_blocks(args.blocks().to_vec()).await?; + db.remove_allows(args.allowed().to_vec()).await?; } else { - db.add_blocks(args.blocks()).await?; - db.add_whitelists(args.whitelists()).await?; + db.add_blocks(args.blocks().to_vec()).await?; + db.add_allows(args.allowed().to_vec()).await?; } return Ok(()); } - let media = Media::new(db.clone()); - let state = State::hydrate(config.clone(), &db).await?; + let media = MediaCache::new(db.clone()); + let state = State::build(config.clone(), db.clone()).await?; let actors = ActorCache::new(db.clone()); let job_server = create_server(); - notify::Notifier::new(config.database_url().parse()?) - .register(notify::NewBlocks(state.clone())) - .register(notify::NewWhitelists(state.clone())) - .register(notify::NewListeners(state.clone(), job_server.clone())) - .register(notify::NewActors(actors.clone())) - .register(notify::NewNodes(state.node_cache())) - .register(notify::RmBlocks(state.clone())) - .register(notify::RmWhitelists(state.clone())) - .register(notify::RmListeners(state.clone())) - .register(notify::RmActors(actors.clone())) - .register(notify::RmNodes(state.node_cache())) - .start(); - - if args.jobs_only() { - for _ in 0..num_cpus::get() { - let state = state.clone(); - let actors = actors.clone(); - let job_server = job_server.clone(); - let media = media.clone(); - let config = config.clone(); - let db = db.clone(); - - Arbiter::new().exec_fn(move || { - create_workers(db, state, actors, job_server, media, config); - }); - } - actix_rt::signal::ctrl_c().await?; - return Ok(()); - } - - let no_jobs = args.no_jobs(); + create_workers( + db.clone(), + state.clone(), + actors.clone(), + job_server.clone(), + media.clone(), + config.clone(), + ); let bind_address = config.bind_address(); HttpServer::new(move || { - if !no_jobs { - create_workers( - db.clone(), - state.clone(), - actors.clone(), - job_server.clone(), - media.clone(), - config.clone(), - ); - } - App::new() .wrap(Compress::default()) .wrap(Logger::default()) diff --git a/src/middleware/verifier.rs b/src/middleware/verifier.rs index 3c21aef..e6012ba 100644 --- a/src/middleware/verifier.rs +++ b/src/middleware/verifier.rs @@ -1,11 +1,11 @@ use crate::{ + apub::AcceptedActors, data::{ActorCache, State}, error::MyError, requests::Requests, }; -use activitystreams::uri; +use activitystreams::{base::BaseExt, uri, url::Url}; use actix_web::web; -use futures::join; use http_signature_normalization_actix::{prelude::*, verify::DeprecatedAlgorithm}; use log::error; use rsa::{hash::Hash, padding::PaddingScheme, PublicKey, RSAPublicKey}; @@ -24,47 +24,55 @@ impl MyVerify { signature: String, signing_string: String, ) -> Result { - let mut uri = uri!(key_id); + let public_key_id = uri!(key_id); - let (is_blocked, is_whitelisted) = - join!(self.2.is_blocked(&uri), self.2.is_whitelisted(&uri)); - - if is_blocked { - return Err(MyError::Blocked(key_id)); - } - - if !is_whitelisted { - return Err(MyError::Whitelist(key_id)); - } - - uri.set_fragment(None); - let actor = self.1.get(&uri, &self.0).await?; - let was_cached = actor.is_cached(); - let actor = actor.into_inner(); - - match algorithm { - Some(Algorithm::Hs2019) => (), - Some(Algorithm::Deprecated(DeprecatedAlgorithm::RsaSha256)) => (), - Some(other) => { - return Err(MyError::Algorithm(other.to_string())); + let actor_id = if let Some(mut actor_id) = self + .2 + .db + .actor_id_from_public_key_id(public_key_id.clone()) + .await? + { + if !self.2.db.is_allowed(actor_id.clone()).await? { + return Err(MyError::NotAllowed(key_id)); } - None => (), - }; - let res = do_verify(&actor.public_key, signature.clone(), signing_string.clone()).await; + actor_id.set_fragment(None); + let actor = self.1.get(&actor_id, &self.0).await?; + let was_cached = actor.is_cached(); + let actor = actor.into_inner(); - if let Err(e) = res { - if !was_cached { - return Err(e); + match algorithm { + Some(Algorithm::Hs2019) => (), + Some(Algorithm::Deprecated(DeprecatedAlgorithm::RsaSha256)) => (), + Some(other) => { + return Err(MyError::Algorithm(other.to_string())); + } + None => (), + }; + + let res = do_verify(&actor.public_key, signature.clone(), signing_string.clone()).await; + + if let Err(e) = res { + if !was_cached { + return Err(e); + } + } else { + return Ok(true); } + + actor_id } else { - return Ok(true); - } + self.0 + .fetch_json::(public_key_id.as_str()) + .await? + .actor_id() + .ok_or_else(|| MyError::MissingId)? + }; // Previously we verified the sig from an actor's local cache // // Now we make sure we fetch an updated actor - let actor = self.1.get_no_cache(&uri, &self.0).await?; + let actor = self.1.get_no_cache(&actor_id, &self.0).await?; do_verify(&actor.public_key, signature, signing_string).await?; @@ -72,6 +80,29 @@ impl MyVerify { } } +#[derive(serde::Deserialize)] +#[serde(untagged)] +#[serde(rename_all = "camelCase")] +enum PublicKeyResponse { + PublicKey { + #[allow(dead_code)] + id: Url, + owner: Url, + #[allow(dead_code)] + public_key_pem: String, + }, + Actor(AcceptedActors), +} + +impl PublicKeyResponse { + fn actor_id(&self) -> Option { + match self { + PublicKeyResponse::PublicKey { owner, .. } => Some(owner.clone()), + PublicKeyResponse::Actor(actor) => actor.id_unchecked().map(|url| url.clone()), + } + } +} + async fn do_verify( public_key: &str, signature: String, diff --git a/src/notify.rs b/src/notify.rs deleted file mode 100644 index 69ba5ef..0000000 --- a/src/notify.rs +++ /dev/null @@ -1,263 +0,0 @@ -use crate::{ - data::{ActorCache, NodeCache, State}, - db::listen, - jobs::{JobServer, QueryInstance, QueryNodeinfo}, -}; -use activitystreams::url::Url; -use actix_rt::{spawn, time::delay_for}; -use futures::stream::{poll_fn, StreamExt}; -use log::{debug, error, warn}; -use std::{collections::HashMap, sync::Arc, time::Duration}; -use tokio_postgres::{tls::NoTls, AsyncMessage, Config}; -use uuid::Uuid; - -pub trait Listener { - fn key(&self) -> &str; - - fn execute(&self, payload: &str); -} - -pub struct Notifier { - config: Config, - listeners: HashMap>>, -} - -impl Notifier { - pub fn new(config: Config) -> Self { - Notifier { - config, - listeners: HashMap::new(), - } - } - - pub fn register(mut self, l: L) -> Self - where - L: Listener + Send + Sync + 'static, - { - let v = self - .listeners - .entry(l.key().to_owned()) - .or_insert_with(Vec::new); - v.push(Box::new(l)); - self - } - - pub fn start(self) { - spawn(async move { - let Notifier { config, listeners } = self; - - loop { - let (new_client, mut conn) = match config.connect(NoTls).await { - Ok((client, conn)) => (client, conn), - Err(e) => { - error!("Error establishing DB Connection, {}", e); - delay_for(Duration::new(5, 0)).await; - continue; - } - }; - - let client = Arc::new(new_client); - let new_client = client.clone(); - - spawn(async move { - if let Err(e) = listen(&new_client).await { - error!("Error listening for updates, {}", e); - } - }); - - let mut stream = poll_fn(move |cx| conn.poll_message(cx)); - - loop { - match stream.next().await { - Some(Ok(AsyncMessage::Notification(n))) => { - debug!("Handling Notification, {:?}", n); - if let Some(v) = listeners.get(n.channel()) { - for l in v { - l.execute(n.payload()); - } - } - } - Some(Ok(AsyncMessage::Notice(e))) => { - debug!("Handling Notice, {:?}", e); - } - Some(Ok(_)) => { - debug!("Handling rest"); - } - Some(Err(e)) => { - debug!("Breaking loop due to error Error, {:?}", e); - break; - } - None => { - debug!("End of stream, breaking loop"); - break; - } - } - } - - drop(client); - warn!("Restarting listener task"); - } - }); - } -} - -pub struct NewBlocks(pub State); -pub struct NewWhitelists(pub State); -pub struct NewListeners(pub State, pub JobServer); -pub struct NewActors(pub ActorCache); -pub struct NewNodes(pub NodeCache); -pub struct RmBlocks(pub State); -pub struct RmWhitelists(pub State); -pub struct RmListeners(pub State); -pub struct RmActors(pub ActorCache); -pub struct RmNodes(pub NodeCache); - -impl Listener for NewBlocks { - fn key(&self) -> &str { - "new_blocks" - } - - fn execute(&self, payload: &str) { - debug!("Caching block of {}", payload); - let state = self.0.clone(); - let payload = payload.to_owned(); - spawn(async move { state.cache_block(payload).await }); - } -} - -impl Listener for NewWhitelists { - fn key(&self) -> &str { - "new_whitelists" - } - - fn execute(&self, payload: &str) { - debug!("Caching whitelist of {}", payload); - let state = self.0.clone(); - let payload = payload.to_owned(); - spawn(async move { state.cache_whitelist(payload.to_owned()).await }); - } -} - -impl Listener for NewListeners { - fn key(&self) -> &str { - "new_listeners" - } - - fn execute(&self, payload: &str) { - if let Ok(uri) = payload.parse::() { - debug!("Caching listener {}", uri); - let state = self.0.clone(); - let _ = self.1.queue(QueryInstance::new(uri.clone())); - let _ = self.1.queue(QueryNodeinfo::new(uri.clone())); - spawn(async move { state.cache_listener(uri).await }); - } else { - warn!("Not caching listener {}, parse error", payload); - } - } -} - -impl Listener for NewActors { - fn key(&self) -> &str { - "new_actors" - } - - fn execute(&self, payload: &str) { - if let Ok(uri) = payload.parse::() { - debug!("Caching actor {}", uri); - let actors = self.0.clone(); - spawn(async move { actors.cache_follower(uri).await }); - } else { - warn!("Not caching actor {}, parse error", payload); - } - } -} - -impl Listener for NewNodes { - fn key(&self) -> &str { - "new_nodes" - } - - fn execute(&self, payload: &str) { - if let Ok(uuid) = payload.parse::() { - debug!("Caching node {}", uuid); - let nodes = self.0.clone(); - spawn(async move { nodes.cache_by_id(uuid).await }); - } else { - warn!("Not caching node {}, parse error", payload); - } - } -} - -impl Listener for RmBlocks { - fn key(&self) -> &str { - "rm_blocks" - } - - fn execute(&self, payload: &str) { - debug!("Busting block cache for {}", payload); - let state = self.0.clone(); - let payload = payload.to_owned(); - spawn(async move { state.bust_block(&payload).await }); - } -} - -impl Listener for RmWhitelists { - fn key(&self) -> &str { - "rm_whitelists" - } - - fn execute(&self, payload: &str) { - debug!("Busting whitelist cache for {}", payload); - let state = self.0.clone(); - let payload = payload.to_owned(); - spawn(async move { state.bust_whitelist(&payload).await }); - } -} - -impl Listener for RmListeners { - fn key(&self) -> &str { - "rm_listeners" - } - - fn execute(&self, payload: &str) { - if let Ok(uri) = payload.parse::() { - debug!("Busting listener cache for {}", uri); - let state = self.0.clone(); - spawn(async move { state.bust_listener(&uri).await }); - } else { - warn!("Not busting listener cache for {}", payload); - } - } -} - -impl Listener for RmActors { - fn key(&self) -> &str { - "rm_actors" - } - - fn execute(&self, payload: &str) { - if let Ok(uri) = payload.parse::() { - debug!("Busting actor cache for {}", uri); - let actors = self.0.clone(); - spawn(async move { actors.bust_follower(&uri).await }); - } else { - warn!("Not busting actor cache for {}", payload); - } - } -} - -impl Listener for RmNodes { - fn key(&self) -> &str { - "rm_nodes" - } - - fn execute(&self, payload: &str) { - if let Ok(uuid) = payload.parse::() { - debug!("Caching node {}", uuid); - let nodes = self.0.clone(); - spawn(async move { nodes.bust_by_id(uuid).await }); - } else { - warn!("Not caching node {}, parse error", payload); - } - } -} diff --git a/src/routes/inbox.rs b/src/routes/inbox.rs index 4e51bf0..7dcf53e 100644 --- a/src/routes/inbox.rs +++ b/src/routes/inbox.rs @@ -1,7 +1,8 @@ use crate::{ apub::{AcceptedActivities, AcceptedUndoObjects, UndoTypes, ValidTypes}, config::{Config, UrlKind}, - data::{Actor, ActorCache, State}, + data::{ActorCache, State}, + db::Actor, error::MyError, jobs::apub::{Announce, Follow, Forward, Reject, Undo}, jobs::JobServer, @@ -12,7 +13,6 @@ use activitystreams::{ activity, base::AnyBase, prelude::*, primitives::OneOrMany, public, url::Url, }; use actix_web::{web, HttpResponse}; -use futures::join; use http_signature_normalization_actix::prelude::{DigestVerified, SignatureVerified}; use log::error; @@ -35,21 +35,14 @@ pub async fn route( .await? .into_inner(); - let (is_blocked, is_whitelisted, is_listener) = join!( - state.is_blocked(&actor.id), - state.is_whitelisted(&actor.id), - state.is_listener(&actor.inbox) - ); + let is_allowed = state.db.is_allowed(actor.id.clone()).await?; + let is_connected = state.db.is_connected(actor.id.clone()).await?; - if is_blocked { - return Err(MyError::Blocked(actor.id.to_string())); + if !is_allowed { + return Err(MyError::NotAllowed(actor.id.to_string())); } - if !is_whitelisted { - return Err(MyError::Whitelist(actor.id.to_string())); - } - - if !is_listener && !valid_without_listener(&input)? { + if !is_connected && !valid_without_listener(&input)? { return Err(MyError::NotSubscribed(actor.inbox.to_string())); } @@ -73,9 +66,9 @@ pub async fn route( ValidTypes::Announce | ValidTypes::Create => { handle_announce(&state, &jobs, input, actor).await? } - ValidTypes::Follow => handle_follow(&config, &jobs, input, actor, is_listener).await?, + ValidTypes::Follow => handle_follow(&config, &jobs, input, actor, is_connected).await?, ValidTypes::Delete | ValidTypes::Update => handle_forward(&jobs, input, actor).await?, - ValidTypes::Undo => handle_undo(&config, &jobs, input, actor, is_listener).await?, + ValidTypes::Undo => handle_undo(&config, &jobs, input, actor, is_connected).await?, }; Ok(accepted(serde_json::json!({}))) diff --git a/src/routes/index.rs b/src/routes/index.rs index 3271de8..430ef7d 100644 --- a/src/routes/index.rs +++ b/src/routes/index.rs @@ -8,7 +8,7 @@ pub async fn route( state: web::Data, config: web::Data, ) -> Result { - let mut nodes = state.node_cache().nodes().await; + let mut nodes = state.node_cache().nodes().await?; nodes.shuffle(&mut thread_rng()); let mut buf = BufWriter::new(Vec::new()); diff --git a/src/routes/media.rs b/src/routes/media.rs index 4cdd625..920ab63 100644 --- a/src/routes/media.rs +++ b/src/routes/media.rs @@ -1,4 +1,4 @@ -use crate::{data::Media, error::MyError, requests::Requests}; +use crate::{data::MediaCache, error::MyError, requests::Requests}; use actix_web::{ http::header::{CacheControl, CacheDirective}, web, HttpResponse, @@ -6,13 +6,13 @@ use actix_web::{ use uuid::Uuid; pub async fn route( - media: web::Data, + media: web::Data, requests: web::Data, uuid: web::Path, ) -> Result { let uuid = uuid.into_inner(); - if let Some((content_type, bytes)) = media.get_bytes(uuid).await { + if let Some((content_type, bytes)) = media.get_bytes(uuid).await? { return Ok(cached(content_type, bytes)); } @@ -21,7 +21,7 @@ pub async fn route( media .store_bytes(uuid, content_type.clone(), bytes.clone()) - .await; + .await?; return Ok(cached(content_type, bytes)); } diff --git a/src/routes/nodeinfo.rs b/src/routes/nodeinfo.rs index 0527168..8ba875e 100644 --- a/src/routes/nodeinfo.rs +++ b/src/routes/nodeinfo.rs @@ -46,14 +46,16 @@ pub async fn route(config: web::Data, state: web::Data) -> web::J }, metadata: Metadata { peers: state - .listeners() + .db + .inboxes() .await + .unwrap_or(vec![]) .iter() .filter_map(|listener| listener.domain()) .map(|s| s.to_owned()) .collect(), blocks: if config.publish_blocks() { - Some(state.blocks().await) + Some(state.db.blocks().await.unwrap_or(vec![])) } else { None }, diff --git a/src/schema.rs b/src/schema.rs deleted file mode 100644 index e358f4c..0000000 --- a/src/schema.rs +++ /dev/null @@ -1,99 +0,0 @@ -table! { - actors (id) { - id -> Uuid, - actor_id -> Text, - public_key -> Text, - public_key_id -> Text, - listener_id -> Uuid, - created_at -> Timestamp, - updated_at -> Timestamp, - } -} - -table! { - blocks (id) { - id -> Uuid, - domain_name -> Text, - created_at -> Timestamp, - updated_at -> Nullable, - } -} - -table! { - jobs (id) { - id -> Uuid, - job_id -> Uuid, - job_queue -> Text, - job_timeout -> Int8, - job_updated -> Timestamp, - job_status -> Text, - job_value -> Jsonb, - job_next_run -> Nullable, - created_at -> Timestamp, - updated_at -> Timestamp, - } -} - -table! { - listeners (id) { - id -> Uuid, - actor_id -> Text, - created_at -> Timestamp, - updated_at -> Nullable, - } -} - -table! { - media (id) { - id -> Uuid, - media_id -> Uuid, - url -> Text, - created_at -> Timestamp, - updated_at -> Timestamp, - } -} - -table! { - nodes (id) { - id -> Uuid, - listener_id -> Uuid, - nodeinfo -> Nullable, - instance -> Nullable, - contact -> Nullable, - created_at -> Timestamp, - updated_at -> Timestamp, - } -} - -table! { - settings (id) { - id -> Uuid, - key -> Text, - value -> Text, - created_at -> Timestamp, - updated_at -> Nullable, - } -} - -table! { - whitelists (id) { - id -> Uuid, - domain_name -> Text, - created_at -> Timestamp, - updated_at -> Nullable, - } -} - -joinable!(actors -> listeners (listener_id)); -joinable!(nodes -> listeners (listener_id)); - -allow_tables_to_appear_in_same_query!( - actors, - blocks, - jobs, - listeners, - media, - nodes, - settings, - whitelists, -); diff --git a/templates/admin.rs.html b/templates/admin.rs.html index af54435..53d5bce 100644 --- a/templates/admin.rs.html +++ b/templates/admin.rs.html @@ -1,4 +1,4 @@ -@use crate::data::Contact; +@use crate::db::Contact; @use activitystreams::url::Url; @(contact: &Contact, base: &Url) diff --git a/templates/info.rs.html b/templates/info.rs.html index 5606ba6..6120c57 100644 --- a/templates/info.rs.html +++ b/templates/info.rs.html @@ -1,4 +1,4 @@ -@use crate::data::Info; +@use crate::db::Info; @use activitystreams::url::Url; @(info: &Info, base: &Url) diff --git a/templates/instance.rs.html b/templates/instance.rs.html index 5529577..d08bcfc 100644 --- a/templates/instance.rs.html +++ b/templates/instance.rs.html @@ -1,4 +1,4 @@ -@use crate::{data::{Contact, Instance}, templates::admin}; +@use crate::{db::{Contact, Instance}, templates::admin}; @use activitystreams::url::Url; @(instance: &Instance, software: Option<&str>, contact: Option<&Contact>, base: &Url)