From 9ea2bd9f2fd2002361c5a74d4dd0ef2559d90bb1 Mon Sep 17 00:00:00 2001 From: Paul Makles Date: Sat, 29 Jun 2024 17:35:47 +0100 Subject: [PATCH] feat: implement apple push notifications --- Cargo.lock | 385 +++++++++++++++--- crates/core/config/Revolt.toml | 5 + crates/core/config/src/lib.rs | 10 + crates/core/database/Cargo.toml | 3 + crates/core/database/src/models/users/ops.rs | 3 + .../database/src/models/users/ops/mongodb.rs | 19 + .../src/models/users/ops/reference.rs | 5 + .../database/src/tasks/apple_notifications.rs | 113 +++++ crates/core/database/src/tasks/mod.rs | 3 + crates/core/database/src/tasks/web_push.rs | 12 + 10 files changed, 499 insertions(+), 59 deletions(-) create mode 100644 crates/core/database/src/tasks/apple_notifications.rs diff --git a/Cargo.lock b/Cargo.lock index 92da288e4..8623be678 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.9.4" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6" +checksum = "bc3be92e19a7ef47457b8e6f90707e12b6ac5d20c6f3866584fa3be0787d839f" dependencies = [ "aead", "aes", @@ -457,6 +457,12 @@ version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -561,7 +567,7 @@ dependencies = [ "base64 0.13.0", "chrono", "hex", - "indexmap", + "indexmap 1.9.3", "lazy_static", "rand 0.8.5", "serde", @@ -898,9 +904,9 @@ dependencies = [ [[package]] name = "ctr" -version = "0.8.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea" +checksum = "a232f92a03f37dd7d7dd2adc67166c77e9cd88de5b019b9a9eecfaeaf7bfd481" dependencies = [ "cipher", ] @@ -1297,6 +1303,12 @@ dependencies = [ "termcolor", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "erased-serde" version = "0.3.20" @@ -1625,13 +1637,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "libc", - "wasi 0.10.0+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -1715,8 +1727,27 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", - "indexmap", + "http 0.2.7", + "indexmap 1.9.3", + "slab", + "tokio 1.35.1", + "tokio-util 0.7.2", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes 1.5.0", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap 2.0.1", "slab", "tokio 1.35.1", "tokio-util 0.7.2", @@ -1856,6 +1887,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes 1.5.0", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.5" @@ -1863,7 +1905,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes 1.5.0", - "http", + "http 0.2.7", + "pin-project-lite 0.2.13", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes 1.5.0", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes 1.5.0", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", "pin-project-lite 0.2.13", ] @@ -1898,9 +1963,9 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.13", + "http 0.2.7", + "http-body 0.4.5", "httparse", "httpdate", "itoa", @@ -1912,6 +1977,42 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +dependencies = [ + "bytes 1.5.0", + "futures-channel", + "futures-util", + "h2 0.4.5", + "http 1.1.0", + "http-body 1.0.0", + "pin-project-lite 0.2.13", + "smallvec", + "tokio 1.35.1", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper 1.3.1", + "hyper-util", + "rustls 0.22.4", + "rustls-pki-types", + "tokio 1.35.1", + "tokio-rustls 0.25.0", + "tower-service", + "webpki-roots 0.26.3", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1919,12 +2020,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes 1.5.0", - "hyper", + "hyper 0.14.19", "native-tls", "tokio 1.35.1", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b875924a60b96e5d7b9ae7b066540b1dd1cbd90d1828f54c92e02a283351c56" +dependencies = [ + "bytes 1.5.0", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.0", + "hyper 1.3.1", + "pin-project-lite 0.2.13", + "socket2 0.5.5", + "tokio 1.35.1", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1975,6 +2096,16 @@ dependencies = [ "serde", ] +[[package]] +name = "indexmap" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad227c3af19d4914570ad36d30409928b75967c298feb9ea1969db3a610bb14e" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + [[package]] name = "inlinable_string" version = "0.1.15" @@ -2022,7 +2153,7 @@ dependencies = [ "encoding_rs", "event-listener 2.5.2", "futures-lite", - "http", + "http 0.2.7", "log", "mime", "once_cell", @@ -2461,8 +2592,8 @@ dependencies = [ "percent-encoding", "rand 0.8.5", "rustc_version_runtime", - "rustls", - "rustls-pemfile", + "rustls 0.20.6", + "rustls-pemfile 0.3.0", "serde", "serde_with", "sha-1 0.10.0", @@ -2473,13 +2604,13 @@ dependencies = [ "take_mut", "thiserror", "tokio 1.35.1", - "tokio-rustls", + "tokio-rustls 0.23.4", "tokio-util 0.7.2", "trust-dns-proto", "trust-dns-resolver", "typed-builder", "uuid 0.8.2", - "webpki-roots", + "webpki-roots 0.22.3", ] [[package]] @@ -2491,7 +2622,7 @@ dependencies = [ "bytes 1.5.0", "encoding_rs", "futures-util", - "http", + "http 0.2.7", "httparse", "log", "memchr", @@ -2856,6 +2987,16 @@ dependencies = [ "base64 0.13.0", ] +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + [[package]] name = "pem-rfc7468" version = "0.6.0" @@ -3415,10 +3556,10 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", + "h2 0.3.13", + "http 0.2.7", + "http-body 0.4.5", + "hyper 0.14.19", "hyper-tls", "ipnet", "js-sys", @@ -3511,7 +3652,7 @@ dependencies = [ "decancer", "fcm", "futures", - "indexmap", + "indexmap 1.9.3", "isahc", "iso8601-timestamp 0.2.11", "linkify 0.8.1", @@ -3528,6 +3669,7 @@ dependencies = [ "revolt-permissions", "revolt-presence", "revolt-result", + "revolt_a2", "revolt_okapi", "revolt_optional_struct", "revolt_rocket_okapi", @@ -3593,7 +3735,7 @@ dependencies = [ name = "revolt-models" version = "0.7.12" dependencies = [ - "indexmap", + "indexmap 1.9.3", "iso8601-timestamp 0.2.11", "once_cell", "regex", @@ -3644,6 +3786,30 @@ dependencies = [ "serde_json", ] +[[package]] +name = "revolt_a2" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "466eb5262fcbb26e6e10b8a0acf56eb7cc095008132ff4ebddfc44d8672f8066" +dependencies = [ + "base64 0.21.3", + "erased-serde", + "http 1.1.0", + "http-body-util", + "hyper 1.3.1", + "hyper-rustls", + "hyper-util", + "parking_lot", + "pem 3.0.4", + "ring 0.17.3", + "rustls 0.22.4", + "rustls-pemfile 2.1.2", + "serde", + "serde_json", + "thiserror", + "tokio 1.35.1", +] + [[package]] name = "revolt_okapi" version = "0.9.1" @@ -3716,11 +3882,25 @@ dependencies = [ "libc", "once_cell", "spin 0.5.2", - "untrusted", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babe80d5c16becf6594aa32ad2be8fe08498e7ae60b77de8df700e67f191d7e" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin 0.9.3", + "untrusted 0.9.0", + "windows-sys 0.48.0", +] + [[package]] name = "rmp" version = "0.8.11" @@ -3758,7 +3938,7 @@ dependencies = [ "either", "figment", "futures", - "indexmap", + "indexmap 1.9.3", "log", "memchr", "multer", @@ -3806,7 +3986,7 @@ checksum = "d6aeb6bb9c61e9cd2c00d70ea267bf36f76a4cc615e5908b349c2f9d93999b47" dependencies = [ "devise", "glob", - "indexmap", + "indexmap 1.9.3", "proc-macro2", "quote 1.0.26", "rocket_http", @@ -3819,7 +3999,7 @@ name = "rocket_cors" version = "0.6.0-alpha1" source = "git+https://github.com/lawliet89/rocket_cors?rev=c17e8145baa4790319fdb6a473e465b960f55e7c#c17e8145baa4790319fdb6a473e465b960f55e7c" dependencies = [ - "http", + "http 0.2.7", "log", "regex", "rocket", @@ -3850,9 +4030,9 @@ dependencies = [ "cookie", "either", "futures", - "http", - "hyper", - "indexmap", + "http 0.2.7", + "hyper 0.14.19", + "indexmap 1.9.3", "log", "memchr", "pear", @@ -3973,11 +4153,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" dependencies = [ "log", - "ring", + "ring 0.16.20", "sct", "webpki", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring 0.17.3", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "0.3.0" @@ -3987,6 +4181,33 @@ dependencies = [ "base64 0.13.0", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + +[[package]] +name = "rustls-webpki" +version = "0.102.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +dependencies = [ + "ring 0.17.3", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "rustversion" version = "1.0.11" @@ -4016,7 +4237,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1847b767a3d62d95cbf3d8a9f0e421cf57a0d8aa4f411d4b16525afb0284d4ed" dependencies = [ "dyn-clone", - "indexmap", + "indexmap 1.9.3", "schemars_derive", "serde", "serde_json", @@ -4052,8 +4273,8 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -4279,7 +4500,7 @@ version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ - "indexmap", + "indexmap 1.9.3", "itoa", "ryu", "serde", @@ -4416,9 +4637,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" @@ -4498,9 +4719,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.4.1" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -4738,11 +4959,22 @@ version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls", + "rustls 0.20.6", "tokio 1.35.1", "webpki", ] +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", + "tokio 1.35.1", +] + [[package]] name = "tokio-stream" version = "0.1.8" @@ -4804,6 +5036,27 @@ dependencies = [ "sha2", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite 0.2.13", + "tokio 1.35.1", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.1" @@ -4812,11 +5065,10 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.34" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "log", "pin-project-lite 0.2.13", "tracing-attributes", @@ -4825,22 +5077,22 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.21" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote 1.0.26", - "syn 1.0.107", + "syn 2.0.15", ] [[package]] name = "tracing-core" -version = "0.1.26" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ - "lazy_static", + "once_cell", "valuable", ] @@ -4943,7 +5195,7 @@ dependencies = [ "base64 0.13.0", "byteorder", "bytes 1.5.0", - "http", + "http 0.2.7", "httparse", "log", "rand 0.8.5", @@ -5094,9 +5346,9 @@ checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" [[package]] name = "universal-hash" -version = "0.4.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" dependencies = [ "generic-array 0.14.5", "subtle", @@ -5108,6 +5360,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "ureq" version = "2.7.1" @@ -5382,7 +5640,7 @@ dependencies = [ "chrono", "ece", "futures-lite", - "http", + "http 0.2.7", "isahc", "jwt-simple", "log", @@ -5409,8 +5667,8 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" dependencies = [ - "ring", - "untrusted", + "ring 0.16.20", + "untrusted 0.7.1", ] [[package]] @@ -5422,6 +5680,15 @@ dependencies = [ "webpki", ] +[[package]] +name = "webpki-roots" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "wepoll-ffi" version = "0.1.2" diff --git a/crates/core/config/Revolt.toml b/crates/core/config/Revolt.toml index 3fa1fe804..611edfa07 100644 --- a/crates/core/config/Revolt.toml +++ b/crates/core/config/Revolt.toml @@ -29,6 +29,11 @@ public_key = "BGcvgR-i2z4IQ5Mw841vJvkLjt8wY-FjmWrw83jOLCY52qcGZS0OF7nfLzuYbjsQIS [api.fcm] api_key = "" +[api.apn] +pkcs8 = "" +key_id = "" +team_id = "" + [api.security] authifier_shield_key = "" voso_legacy_token = "" diff --git a/crates/core/config/src/lib.rs b/crates/core/config/src/lib.rs index 701fc14d1..46cfb82e6 100644 --- a/crates/core/config/src/lib.rs +++ b/crates/core/config/src/lib.rs @@ -6,6 +6,8 @@ use futures_locks::RwLock; use once_cell::sync::Lazy; use serde::Deserialize; +pub use sentry::capture_error; + #[cfg(not(debug_assertions))] use std::env; @@ -75,6 +77,13 @@ pub struct ApiFcm { pub api_key: String, } +#[derive(Deserialize, Debug, Clone)] +pub struct ApiApn { + pub pkcs8: String, + pub key_id: String, + pub team_id: String, +} + #[derive(Deserialize, Debug, Clone)] pub struct ApiSecurityCaptcha { pub hcaptcha_key: String, @@ -100,6 +109,7 @@ pub struct Api { pub smtp: ApiSmtp, pub vapid: ApiVapid, pub fcm: ApiFcm, + pub apn: ApiApn, pub security: ApiSecurity, pub workers: ApiWorkers, } diff --git a/crates/core/database/Cargo.toml b/crates/core/database/Cargo.toml index 16615f165..000786a6c 100644 --- a/crates/core/database/Cargo.toml +++ b/crates/core/database/Cargo.toml @@ -87,6 +87,9 @@ revolt_rocket_okapi = { version = "0.9.1", optional = true } # Notifications fcm = "0.9.2" web-push = "0.10.0" +revolt_a2 = { version = "0.10.0", default-features = false, features = [ + "ring", +] } # Authifier authifier = { version = "1.0.8" } diff --git a/crates/core/database/src/models/users/ops.rs b/crates/core/database/src/models/users/ops.rs index 62c7fd7cd..fe6bb2ff9 100644 --- a/crates/core/database/src/models/users/ops.rs +++ b/crates/core/database/src/models/users/ops.rs @@ -58,4 +58,7 @@ pub trait AbstractUsers: Sync + Send { /// Delete a user by their id async fn delete_user(&self, id: &str) -> Result<()>; + + /// Remove push subscription for a session by session id (TODO: remove) + async fn remove_push_subscription_by_session_id(&self, session_id: &str) -> Result<()>; } diff --git a/crates/core/database/src/models/users/ops/mongodb.rs b/crates/core/database/src/models/users/ops/mongodb.rs index 4f812a15b..8e132eb11 100644 --- a/crates/core/database/src/models/users/ops/mongodb.rs +++ b/crates/core/database/src/models/users/ops/mongodb.rs @@ -316,6 +316,25 @@ impl AbstractUsers for MongoDb { async fn delete_user(&self, id: &str) -> Result<()> { query!(self, delete_one_by_id, COL, id).map(|_| ()) } + + /// Remove push subscription for a session by session id (TODO: remove) + async fn remove_push_subscription_by_session_id(&self, session_id: &str) -> Result<()> { + self.col::("sessions") + .update_one( + doc! { + "_id": session_id + }, + doc! { + "$unset": { + "subscription": 1 + } + }, + None, + ) + .await + .map(|_| ()) + .map_err(|_| create_database_error!("update_one", COL)) + } } impl IntoDocumentPath for FieldsUser { diff --git a/crates/core/database/src/models/users/ops/reference.rs b/crates/core/database/src/models/users/ops/reference.rs index 470282765..503490804 100644 --- a/crates/core/database/src/models/users/ops/reference.rs +++ b/crates/core/database/src/models/users/ops/reference.rs @@ -163,4 +163,9 @@ impl AbstractUsers for ReferenceDb { Err(create_error!(NotFound)) } } + + /// Remove push subscription for a session by session id (TODO: remove) + async fn remove_push_subscription_by_session_id(&self, _session_id: &str) -> Result<()> { + todo!() + } } diff --git a/crates/core/database/src/tasks/apple_notifications.rs b/crates/core/database/src/tasks/apple_notifications.rs new file mode 100644 index 000000000..cbf9a634c --- /dev/null +++ b/crates/core/database/src/tasks/apple_notifications.rs @@ -0,0 +1,113 @@ +use std::io::Cursor; + +use base64::{ + engine::{self}, + Engine as _, +}; +use deadqueue::limited::Queue; +use once_cell::sync::Lazy; +use revolt_a2::{Client, ClientConfig, DefaultNotificationBuilder}; +use revolt_a2::{Error, ErrorBody, ErrorReason, NotificationBuilder, Response}; +use revolt_config::config; +use revolt_models::v0::PushNotification; + +use crate::Database; + +/// Task information +#[derive(Debug)] +pub struct ApnTask { + /// Session Id + session_id: String, + + /// Device token + device_token: String, + + /// Title + title: String, + + /// Body + body: String, + + /// Thread Id + thread_id: String, +} + +impl ApnTask { + pub fn from_notification( + session_id: String, + device_token: String, + notification: &PushNotification, + ) -> ApnTask { + ApnTask { + session_id, + device_token, + title: notification.author.to_string(), + body: notification.body.to_string(), + thread_id: notification.tag.to_string(), + } + } +} + +static Q: Lazy> = Lazy::new(|| Queue::new(10_000)); + +/// Queue a new task for a worker +pub async fn queue(task: ApnTask) { + Q.try_push(task).ok(); + info!("Queue is using {} slots from {}.", Q.len(), Q.capacity()); +} + +/// Start a new worker +pub async fn worker(db: Database) { + let config = config().await; + if config.api.apn.pkcs8.is_empty() + || config.api.apn.key_id.is_empty() + || config.api.apn.team_id.is_empty() + { + eprintln!("Missing APN keys."); + return; + } + + let pkcs8 = engine::general_purpose::STANDARD + .decode(config.api.apn.pkcs8) + .expect("valid `pcks8`"); + + let client = Client::token( + &mut Cursor::new(pkcs8), + config.api.apn.key_id, + config.api.apn.team_id, + ClientConfig::default(), + ) + .expect("could not create APN client"); + + loop { + let task = Q.pop().await; + let payload = DefaultNotificationBuilder::new() + .set_title(&task.title) + .set_body(&task.body) + .set_thread_id(&task.thread_id) + .build(&task.device_token, Default::default()); + + if let Err(err) = client.send(payload).await { + match err { + Error::ResponseError(Response { + error: + Some(ErrorBody { + reason: ErrorReason::BadDeviceToken | ErrorReason::Unregistered, + .. + }), + .. + }) => { + if let Err(err) = db + .remove_push_subscription_by_session_id(&task.session_id) + .await + { + revolt_config::capture_error(&err); + } + } + err => { + revolt_config::capture_error(&err); + } + } + } + } +} diff --git a/crates/core/database/src/tasks/mod.rs b/crates/core/database/src/tasks/mod.rs index 12a5d122a..24a090ed5 100644 --- a/crates/core/database/src/tasks/mod.rs +++ b/crates/core/database/src/tasks/mod.rs @@ -8,12 +8,15 @@ use std::time::Instant; const WORKER_COUNT: usize = 5; pub mod ack; +pub mod apple_notifications; pub mod last_message_id; pub mod process_embeds; pub mod web_push; /// Spawn background workers pub async fn start_workers(db: Database, authifier_db: authifier::Database) { + task::spawn(apple_notifications::worker(db.clone())); + for _ in 0..WORKER_COUNT { task::spawn(ack::worker(db.clone())); task::spawn(last_message_id::worker(db.clone())); diff --git a/crates/core/database/src/tasks/web_push.rs b/crates/core/database/src/tasks/web_push.rs index 8a055afe6..efe5fd7ea 100644 --- a/crates/core/database/src/tasks/web_push.rs +++ b/crates/core/database/src/tasks/web_push.rs @@ -6,6 +6,7 @@ use base64::{ Engine as _, }; use deadqueue::limited::Queue; +use fcm::FcmError; use once_cell::sync::Lazy; use revolt_config::config; use revolt_models::v0::PushNotification; @@ -16,6 +17,8 @@ use web_push::{ WebPushClient, WebPushMessageBuilder, }; +use super::apple_notifications; + /// Task information #[derive(Debug)] struct PushTask { @@ -101,6 +104,15 @@ pub async fn worker(db: Database) { } else { info!("No FCM token was specified!"); } + } else if sub.endpoint == "apn" { + apple_notifications::queue( + apple_notifications::ApnTask::from_notification( + session.id, + sub.auth, + &task.payload, + ), + ) + .await; } else { // Use Web Push Standard let subscription = SubscriptionInfo {