diff --git a/Cargo.lock b/Cargo.lock index be9eef94..d67eac48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -443,8 +443,6 @@ dependencies = [ [[package]] name = "authifier" version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a03b810b342b4c584cdc1cfc40d1ec763b1d653ef4086f7494740f35d9e1f0" dependencies = [ "async-std", "async-trait", @@ -453,7 +451,7 @@ dependencies = [ "chrono", "futures", "handlebars", - "iso8601-timestamp", + "iso8601-timestamp 0.2.17", "lazy_static", "lettre", "log", @@ -462,16 +460,14 @@ dependencies = [ "rand 0.8.5", "regex", "reqwest 0.11.27", - "revolt_okapi", - "revolt_rocket_okapi", "rocket", "rust-argon2", - "schemars 0.8.22", "serde", "serde_json", "sha1", "totp-lite", "ulid 0.5.0", + "utoipa", "validator 0.15.0", ] @@ -951,14 +947,14 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.9" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" dependencies = [ - "async-trait", "axum-core", "axum-macros", "bytes 1.10.1", + "form_urlencoded", "futures-util", "http 1.3.1", "http-body 1.0.1", @@ -972,8 +968,7 @@ dependencies = [ "multer", "percent-encoding", "pin-project-lite 0.2.16", - "rustversion", - "serde", + "serde_core", "serde_json", "serde_path_to_error", "serde_urlencoded", @@ -987,19 +982,17 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.5" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22" dependencies = [ - "async-trait", "bytes 1.10.1", - "futures-util", + "futures-core", "http 1.3.1", "http-body 1.0.1", "http-body-util", "mime", "pin-project-lite 0.2.16", - "rustversion", "sync_wrapper 1.0.2", "tower-layer", "tower-service", @@ -1008,33 +1001,31 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.9.6" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c794b30c904f0a1c2fb7740f7df7f7972dfaa14ef6f57cb6178dc63e5dca2f04" +checksum = "5136e6c5e7e7978fe23e9876fb924af2c0f84c72127ac6ac17e7c46f457d362c" dependencies = [ "axum", "axum-core", "bytes 1.10.1", - "fastrand 2.3.0", + "futures-core", "futures-util", "headers", "http 1.3.1", "http-body 1.0.1", "http-body-util", "mime", - "multer", "pin-project-lite 0.2.16", - "serde", - "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] name = "axum-macros" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" dependencies = [ "proc-macro2", "quote 1.0.40", @@ -1043,32 +1034,34 @@ dependencies = [ [[package]] name = "axum_typed_multipart" -version = "0.12.2" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05679dcdfeccf4cc6a8f45b80318fff8a9908337da1cfeb6acca00562a345b4d" +checksum = "a3e5345def2a33cfc056ece2751f3e255597691e84bdf7f4380086010495bd4b" dependencies = [ "anyhow", + "async-trait", "axum", "axum_typed_multipart_macros", "bytes 1.10.1", "chrono", "futures-core", "futures-util", + "rust_decimal", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.16", "tokio 1.47.1", "uuid", ] [[package]] name = "axum_typed_multipart_macros" -version = "0.12.2" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11eeb275b20a4c750c9fe7bf5a750e97e7944563003efd1c82e70c229a612ca1" +checksum = "15c97c70acc884855204b16861765d0c7e9765ab7cfcfbe6dcc36b32d703ac87" dependencies = [ "darling 0.20.11", "heck", - "proc-macro-error", + "proc-macro-error2", "quote 1.0.40", "syn 2.0.106", "ubyte", @@ -1263,6 +1256,29 @@ dependencies = [ "piper", ] +[[package]] +name = "borsh" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +dependencies = [ + "once_cell", + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote 1.0.40", + "syn 2.0.106", +] + [[package]] name = "bson" version = "2.15.0" @@ -1275,7 +1291,7 @@ dependencies = [ "getrandom 0.2.16", "getrandom 0.3.3", "hex", - "indexmap 2.11.1", + "indexmap 2.12.0", "js-sys", "once_cell", "rand 0.9.2", @@ -1298,6 +1314,28 @@ version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote 1.0.40", + "syn 1.0.109", +] + [[package]] name = "bytemuck" version = "1.23.2" @@ -1428,6 +1466,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.42" @@ -3031,7 +3075,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.11.1", + "indexmap 2.12.0", "slab", "tokio 1.47.1", "tokio-util", @@ -3050,7 +3094,7 @@ dependencies = [ "futures-core", "futures-sink", "http 1.3.1", - "indexmap 2.11.1", + "indexmap 2.12.0", "slab", "tokio 1.47.1", "tokio-util", @@ -3117,6 +3161,12 @@ dependencies = [ "foldhash", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "headers" version = "0.4.1" @@ -3759,13 +3809,14 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", - "hashbrown 0.15.5", + "hashbrown 0.16.0", "serde", + "serde_core", ] [[package]] @@ -3898,11 +3949,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d4e5d712dd664b11e778d1cfc06c79ba2700d6bc1771e44fb7b6a4656b487d" dependencies = [ "generic-array 1.2.0", - "schemars 0.8.22", "serde", "time", ] +[[package]] +name = "iso8601-timestamp" +version = "0.4.0" +dependencies = [ + "generic-array 1.2.0", + "serde_core", + "time", + "utoipa", +] + [[package]] name = "itertools" version = "0.12.1" @@ -4541,9 +4601,9 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "maybe-rayon" @@ -4975,7 +5035,7 @@ version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote 1.0.40", "syn 1.0.109", @@ -4987,7 +5047,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote 1.0.40", "syn 2.0.106", @@ -5482,7 +5542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64 0.22.1", - "indexmap 2.11.1", + "indexmap 2.12.0", "quick-xml", "serde", "time", @@ -5631,6 +5691,15 @@ dependencies = [ "toml_edit 0.19.15", ] +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.5", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -5732,6 +5801,26 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote 1.0.40", + "syn 1.0.109", +] + [[package]] name = "pxfm" version = "0.1.23" @@ -6170,6 +6259,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + [[package]] name = "reqwest" version = "0.11.27" @@ -6346,8 +6444,8 @@ dependencies = [ name = "revolt-coalesced" version = "0.8.9" dependencies = [ - "indexmap 1.9.3", - "lru 0.7.8", + "indexmap 2.12.0", + "lru 0.12.5", "tokio 1.47.1", ] @@ -6396,9 +6494,9 @@ dependencies = [ "deadqueue", "decancer", "futures", - "indexmap 1.9.3", + "indexmap 2.12.0", "isahc", - "iso8601-timestamp", + "iso8601-timestamp 0.4.0", "linkify 0.8.1", "log", "lru 0.11.1", @@ -6414,16 +6512,14 @@ dependencies = [ "revolt-permissions", "revolt-presence", "revolt-result", - "revolt_okapi", "revolt_optional_struct", - "revolt_rocket_okapi", "rocket", - "schemars 0.8.22", "serde", "serde_json", "ulid 1.2.1", "unicode-segmentation", "url-escape", + "utoipa", "validator 0.16.1", ] @@ -6441,7 +6537,7 @@ dependencies = [ "env_logger", "futures", "impl_ops", - "iso8601-timestamp", + "iso8601-timestamp 0.4.0", "lettre", "linkify 0.6.0", "log", @@ -6460,17 +6556,17 @@ dependencies = [ "revolt-presence", "revolt-ratelimits", "revolt-result", - "revolt_rocket_okapi", "rocket", "rocket_authifier", "rocket_cors", "rocket_empty", "rocket_prometheus", - "schemars 0.8.22", "serde", "serde_json", "ulid 0.4.1", "url", + "utoipa", + "utoipa-scalar", "validator 0.16.1", "vergen", ] @@ -6552,8 +6648,8 @@ dependencies = [ name = "revolt-models" version = "0.8.9" dependencies = [ - "indexmap 1.9.3", - "iso8601-timestamp", + "indexmap 2.12.0", + "iso8601-timestamp 0.4.0", "num_enum 0.6.1", "once_cell", "regex", @@ -6561,7 +6657,6 @@ dependencies = [ "revolt-permissions", "revolt_optional_struct", "rocket", - "schemars 0.8.22", "serde", "utoipa", "validator 0.16.1", @@ -6585,8 +6680,8 @@ dependencies = [ "num_enum 0.6.1", "once_cell", "revolt-result", - "schemars 0.8.22", "serde", + "utoipa", ] [[package]] @@ -6612,7 +6707,7 @@ dependencies = [ "base64 0.22.1", "fcm_v1", "isahc", - "iso8601-timestamp", + "iso8601-timestamp 0.4.0", "log", "pretty_env_logger", "revolt-config", @@ -6651,10 +6746,7 @@ name = "revolt-result" version = "0.8.9" dependencies = [ "axum", - "revolt_okapi", - "revolt_rocket_okapi", "rocket", - "schemars 0.8.22", "serde", "serde_json", "utoipa", @@ -6785,6 +6877,35 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rkyv" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b" +dependencies = [ + "bitvec", + "bytecheck", + "bytes 1.10.1", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0" +dependencies = [ + "proc-macro2", + "quote 1.0.40", + "syn 1.0.109", +] + [[package]] name = "rmp" version = "0.8.14" @@ -6821,7 +6942,7 @@ dependencies = [ "either", "figment", "futures", - "indexmap 2.11.1", + "indexmap 2.12.0", "log", "memchr", "multer", @@ -6848,17 +6969,13 @@ dependencies = [ [[package]] name = "rocket_authifier" version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1f98a5be96ac4144ee1adc809c1594e8212032b8a08d9bc4cef70735dfd976e" dependencies = [ "authifier", - "iso8601-timestamp", - "revolt_okapi", - "revolt_rocket_okapi", + "iso8601-timestamp 0.2.17", "rocket", "rocket_empty", - "schemars 0.8.22", "serde", + "utoipa", ] [[package]] @@ -6869,7 +6986,7 @@ checksum = "575d32d7ec1a9770108c879fc7c47815a80073f96ca07ff9525a94fcede1dd46" dependencies = [ "devise", "glob", - "indexmap 2.11.1", + "indexmap 2.12.0", "proc-macro2", "quote 1.0.40", "rocket_http", @@ -6916,7 +7033,7 @@ dependencies = [ "futures", "http 0.2.12", "hyper 0.14.32", - "indexmap 2.11.1", + "indexmap 2.12.0", "log", "memchr", "pear", @@ -7002,6 +7119,22 @@ dependencies = [ "ordered-multimap", ] +[[package]] +name = "rust_decimal" +version = "1.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35affe401787a9bd846712274d97654355d21b2a2c092a3139aabe31e9022282" +dependencies = [ + "arrayvec", + "borsh", + "bytes 1.10.1", + "num-traits", + "rand 0.8.5", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -7238,9 +7371,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" +checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" dependencies = [ "dyn-clone", "ref-cast", @@ -7628,7 +7761,7 @@ version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.11.1", + "indexmap 2.12.0", "itoa", "memchr", "ryu", @@ -7678,9 +7811,9 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.1", + "indexmap 2.12.0", "schemars 0.9.0", - "schemars 1.0.4", + "schemars 1.1.0", "serde", "serde_derive", "serde_json", @@ -8518,7 +8651,7 @@ checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", "serde_spanned", - "toml_datetime", + "toml_datetime 0.6.11", "toml_edit 0.22.27", ] @@ -8531,14 +8664,23 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a197c0ec7d131bfc6f7e82c8442ba1595aeab35da7adbf05b6b73cd06a16b6be" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.11.1", - "toml_datetime", + "indexmap 2.12.0", + "toml_datetime 0.6.11", "winnow 0.5.40", ] @@ -8548,14 +8690,35 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.11.1", + "indexmap 2.12.0", "serde", "serde_spanned", - "toml_datetime", + "toml_datetime 0.6.11", "toml_write", "winnow 0.7.13", ] +[[package]] +name = "toml_edit" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2ad0b7ae9cfeef5605163839cb9221f453399f15cfb5c10be9885fcf56611f9" +dependencies = [ + "indexmap 2.12.0", + "toml_datetime 0.7.1", + "toml_parser", + "winnow 0.7.13", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow 0.7.13", +] + [[package]] name = "toml_write" version = "0.1.2" @@ -9022,11 +9185,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utoipa" -version = "4.2.3" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23" +checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" dependencies = [ - "indexmap 2.11.1", + "indexmap 2.12.0", "serde", "serde_json", "utoipa-gen", @@ -9034,11 +9197,10 @@ dependencies = [ [[package]] name = "utoipa-gen" -version = "4.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20c24e8ab68ff9ee746aad22d39b5535601e6416d1b0feeabf78be986a5c4392" +checksum = "6d79d08d92ab8af4c5e8a6da20c47ae3f61a0f1dabc1997cdf2d082b757ca08b" dependencies = [ - "proc-macro-error", "proc-macro2", "quote 1.0.40", "regex", @@ -9048,11 +9210,12 @@ dependencies = [ [[package]] name = "utoipa-scalar" -version = "0.1.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3ab4b7269d14d93626b0bfedf212f1b0995cb7d13d35daba21d579511e7fae8" +checksum = "59559e1509172f6b26c1cdbc7247c4ddd1ac6560fe94b584f81ee489b141f719" dependencies = [ "axum", + "rocket", "serde", "serde_json", "utoipa", diff --git a/Cargo.toml b/Cargo.toml index 5c320e63..a888bd88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,9 @@ members = [ [patch.crates-io] redis23 = { package = "redis", version = "0.23.3", git = "https://github.com/revoltchat/redis-rs", rev = "523b2937367e17bd0073722bf6e23d06042cb4e4" } -#authifier = { package = "authifier", version = "1.0.10", path = "../authifier/crates/authifier" } -#rocket_authifier = { package = "rocket_authifier", version = "1.0.10", path = "../authifier/crates/rocket_authifier" } +authifier = { package = "authifier", version = "1.0.10", path = "../authifier/crates/authifier" } +rocket_authifier = { package = "rocket_authifier", version = "1.0.10", path = "../authifier/crates/rocket_authifier" } +iso8601-timestamp = { path = "../iso8601-timestamp" } # I'm 99% sure this is overloading the GitHub worker # hence builds have been failing since, let's just diff --git a/crates/core/database/Cargo.toml b/crates/core/database/Cargo.toml index 47095e3d..039bb708 100644 --- a/crates/core/database/Cargo.toml +++ b/crates/core/database/Cargo.toml @@ -15,7 +15,7 @@ mongodb = ["dep:mongodb", "bson", "authifier/database-mongodb"] # ... Other tasks = ["isahc", "linkify", "url-escape"] async-std-runtime = ["async-std", "authifier/async-std-runtime"] -rocket-impl = ["rocket", "schemars", "revolt_okapi", "revolt_rocket_okapi", "authifier/rocket_impl"] +rocket-impl = ["rocket"] axum-impl = ["axum"] redis-is-patched = ["revolt-presence/redis-is-patched"] @@ -46,7 +46,7 @@ ulid = "1.0.0" nanoid = "0.4.0" base64 = "0.21.3" once_cell = "1.17" -indexmap = "1.9.1" +indexmap = "2.12.0" decancer = "1.6.2" deadqueue = "0.2.4" linkify = { optional = true, version = "0.8.1" } @@ -58,7 +58,7 @@ isahc = { optional = true, version = "1.7", features = ["json"] } serde_json = "1" revolt_optional_struct = "0.2.0" serde = { version = "1", features = ["derive"] } -iso8601-timestamp = { version = "0.2.10", features = ["serde", "bson"] } +iso8601-timestamp = { version = "0.4.0", features = ["serde", "bson"] } # Events redis-kiss = { version = "0.1.4" } @@ -81,15 +81,15 @@ async-recursion = "1.0.4" async-std = { version = "1.8.0", features = ["attributes"], optional = true } # Axum Impl -axum = { version = "0.7.5", optional = true } +axum = { version = "0.8.6", optional = true } # Rocket Impl -schemars = { version = "0.8.8", optional = true } rocket = { version = "0.5.1", default-features = false, features = [ "json", ], optional = true } -revolt_okapi = { version = "0.9.1", optional = true } -revolt_rocket_okapi = { version = "0.10.0", optional = true } + +# Openapi Schema +utoipa = { version = "5.4.0", optional = true } # Authifier authifier = { version = "1.0.15" } diff --git a/crates/core/database/src/models/messages/ops/reference.rs b/crates/core/database/src/models/messages/ops/reference.rs index ae5b43f0..154b93f2 100644 --- a/crates/core/database/src/models/messages/ops/reference.rs +++ b/crates/core/database/src/models/messages/ops/reference.rs @@ -247,7 +247,7 @@ impl AbstractMessages for ReferenceDb { let mut messages = self.messages.lock().await; if let Some(message) = messages.get_mut(id) { if let Some(users) = message.reactions.get_mut(emoji) { - users.remove(&user.to_string()); + users.swap_remove(&user.to_string()); } Ok(()) @@ -260,7 +260,7 @@ impl AbstractMessages for ReferenceDb { async fn clear_reaction(&self, id: &str, emoji: &str) -> Result<()> { let mut messages = self.messages.lock().await; if let Some(message) = messages.get_mut(id) { - message.reactions.remove(emoji); + message.reactions.swap_remove(emoji); Ok(()) } else { Err(create_error!(NotFound)) diff --git a/crates/core/database/src/models/users/axum.rs b/crates/core/database/src/models/users/axum.rs index 6b1f2f32..142eabba 100644 --- a/crates/core/database/src/models/users/axum.rs +++ b/crates/core/database/src/models/users/axum.rs @@ -4,7 +4,6 @@ use revolt_result::{create_error, Error, Result}; use crate::{Database, User}; -#[async_trait::async_trait] impl FromRequestParts for User where Database: FromRef, diff --git a/crates/core/database/src/models/users/mod.rs b/crates/core/database/src/models/users/mod.rs index 4e58da72..5bb424a6 100644 --- a/crates/core/database/src/models/users/mod.rs +++ b/crates/core/database/src/models/users/mod.rs @@ -4,8 +4,6 @@ mod model; mod ops; #[cfg(feature = "rocket-impl")] mod rocket; -#[cfg(feature = "rocket-impl")] -mod schema; pub use model::*; pub use ops::*; diff --git a/crates/core/database/src/models/users/schema.rs b/crates/core/database/src/models/users/schema.rs deleted file mode 100644 index 28f5e42e..00000000 --- a/crates/core/database/src/models/users/schema.rs +++ /dev/null @@ -1,31 +0,0 @@ -use revolt_okapi::openapi3::{SecurityScheme, SecuritySchemeData}; -use revolt_rocket_okapi::{ - gen::OpenApiGenerator, - request::{OpenApiFromRequest, RequestHeaderInput}, -}; - -use crate::User; - -impl OpenApiFromRequest<'_> for User { - fn from_request_input( - _gen: &mut OpenApiGenerator, - _name: String, - _required: bool, - ) -> revolt_rocket_okapi::Result { - let mut requirements = schemars::Map::new(); - requirements.insert("Session Token".to_owned(), vec![]); - - Ok(RequestHeaderInput::Security( - "Session Token".to_owned(), - SecurityScheme { - data: SecuritySchemeData::ApiKey { - name: "x-session-token".to_owned(), - location: "header".to_owned(), - }, - description: Some("Used to authenticate as a user.".to_owned()), - extensions: schemars::Map::new(), - }, - requirements, - )) - } -} diff --git a/crates/core/database/src/util/idempotency.rs b/crates/core/database/src/util/idempotency.rs index 769dc1dd..729d9f55 100644 --- a/crates/core/database/src/util/idempotency.rs +++ b/crates/core/database/src/util/idempotency.rs @@ -43,45 +43,45 @@ impl IdempotencyKey { } } -#[cfg(feature = "rocket-impl")] -use revolt_rocket_okapi::{ - gen::OpenApiGenerator, - request::{OpenApiFromRequest, RequestHeaderInput}, - revolt_okapi::openapi3::{Parameter, ParameterValue}, -}; +// #[cfg(feature = "rocket-impl")] +// use revolt_rocket_okapi::{ +// gen::OpenApiGenerator, +// request::{OpenApiFromRequest, RequestHeaderInput}, +// revolt_okapi::openapi3::{Parameter, ParameterValue}, +// }; -#[cfg(feature = "rocket-impl")] -use schemars::schema::{InstanceType, SchemaObject, SingleOrVec}; +// #[cfg(feature = "rocket-impl")] +// use schemars::schema::{InstanceType, SchemaObject, SingleOrVec}; -#[cfg(feature = "rocket-impl")] -impl OpenApiFromRequest<'_> for IdempotencyKey { - fn from_request_input( - _gen: &mut OpenApiGenerator, - _name: String, - _required: bool, - ) -> revolt_rocket_okapi::Result { - Ok(RequestHeaderInput::Parameter(Parameter { - name: "Idempotency-Key".to_string(), - description: Some("Unique key to prevent duplicate requests".to_string()), - allow_empty_value: false, - required: false, - deprecated: false, - extensions: schemars::Map::new(), - location: "header".to_string(), - value: ParameterValue::Schema { - allow_reserved: false, - example: None, - examples: None, - explode: None, - style: None, - schema: SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), - ..Default::default() - }, - }, - })) - } -} +// #[cfg(feature = "rocket-impl")] +// impl OpenApiFromRequest<'_> for IdempotencyKey { +// fn from_request_input( +// _gen: &mut OpenApiGenerator, +// _name: String, +// _required: bool, +// ) -> revolt_rocket_okapi::Result { +// Ok(RequestHeaderInput::Parameter(Parameter { +// name: "Idempotency-Key".to_string(), +// description: Some("Unique key to prevent duplicate requests".to_string()), +// allow_empty_value: false, +// required: false, +// deprecated: false, +// extensions: schemars::Map::new(), +// location: "header".to_string(), +// value: ParameterValue::Schema { +// allow_reserved: false, +// example: None, +// examples: None, +// explode: None, +// style: None, +// schema: SchemaObject { +// instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), +// ..Default::default() +// }, +// }, +// })) +// } +// } #[cfg(feature = "rocket-impl")] use rocket::{ diff --git a/crates/core/database/src/util/mod.rs b/crates/core/database/src/util/mod.rs index 1baf7d8b..fba56dd0 100644 --- a/crates/core/database/src/util/mod.rs +++ b/crates/core/database/src/util/mod.rs @@ -4,3 +4,5 @@ pub mod idempotency; pub mod permissions; pub mod reference; pub mod test_fixtures; +#[cfg(feature = "utoipa")] +pub mod utoipa; \ No newline at end of file diff --git a/crates/core/database/src/util/reference.rs b/crates/core/database/src/util/reference.rs index a0aefc40..65b04e0e 100644 --- a/crates/core/database/src/util/reference.rs +++ b/crates/core/database/src/util/reference.rs @@ -3,10 +3,13 @@ use std::str::FromStr; use revolt_result::Result; #[cfg(feature = "rocket-impl")] use rocket::request::FromParam; -#[cfg(feature = "rocket-impl")] -use schemars::{ - schema::{InstanceType, Schema, SchemaObject, SingleOrVec}, - JsonSchema, +#[cfg(feature = "utoipa")] +use utoipa::{ + openapi::{ + path::{Parameter, ParameterBuilder, ParameterIn}, + Required, + }, + IntoParams, }; use crate::{ @@ -113,16 +116,14 @@ impl<'r> FromParam<'r> for Reference<'r> { } } -#[cfg(feature = "rocket-impl")] -impl<'a> JsonSchema for Reference<'a> { - fn schema_name() -> String { - "Id".to_string() - } - - fn json_schema(_gen: &mut schemars::gen::SchemaGenerator) -> Schema { - Schema::Object(SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), - ..Default::default() - }) +#[cfg(feature = "utoipa")] +impl IntoParams for Reference<'_> { + fn into_params(parameter_in_provider: impl Fn() -> Option) -> Vec { + vec![ParameterBuilder::new() + .name("id") + .required(Required::True) + .description(Some("An ID".to_string())) + .parameter_in(parameter_in_provider().unwrap_or_default()) + .build()] } } diff --git a/crates/core/database/src/util/utoipa.rs b/crates/core/database/src/util/utoipa.rs new file mode 100644 index 00000000..ef3413cd --- /dev/null +++ b/crates/core/database/src/util/utoipa.rs @@ -0,0 +1,21 @@ +use utoipa::{Modify, openapi::{OpenApi, security::{ApiKey, ApiKeyValue, SecurityScheme}}}; + +pub struct TokenSecurity; + +impl Modify for TokenSecurity { + fn modify(&self, openapi: &mut OpenApi) { + let components = openapi.components.get_or_insert_default(); + + components.add_security_scheme( + "Session-Token", + SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new( + "X-Session-Token".to_string(), + ))), + ); + + components.add_security_scheme( + "Bot-Token", + SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("X-Bot-Ticket".to_string()))), + ); + } +} \ No newline at end of file diff --git a/crates/core/models/Cargo.toml b/crates/core/models/Cargo.toml index 4821ec75..765ca568 100644 --- a/crates/core/models/Cargo.toml +++ b/crates/core/models/Cargo.toml @@ -10,22 +10,22 @@ description = "Revolt Backend: API Models" [features] serde = ["dep:serde", "revolt-permissions/serde", "indexmap/serde"] -schemas = ["dep:schemars", "revolt-permissions/schemas"] -utoipa = ["dep:utoipa"] validator = ["dep:validator"] rocket = ["dep:rocket"] -partials = ["dep:revolt_optional_struct", "serde", "schemas", "utoipa"] +partials = ["dep:revolt_optional_struct", "serde", "utoipa"] default = ["serde", "partials", "rocket"] [dependencies] # Core revolt-config = { version = "0.8.9", path = "../config" } -revolt-permissions = { version = "0.8.9", path = "../permissions" } +revolt-permissions = { version = "0.8.9", path = "../permissions", features = [ + "utoipa", +] } # Utility regex = "1.11" -indexmap = "1.9.3" +indexmap = "2.12.0" once_cell = "1.17.1" num_enum = "0.6.1" @@ -35,11 +35,10 @@ rocket = { optional = true, version = "0.5.0-rc.2", default-features = false } # Serialisation revolt_optional_struct = { version = "0.2.0", optional = true } serde = { version = "1", features = ["derive"], optional = true } -iso8601-timestamp = { version = "0.2.11", features = ["schema", "bson"] } +iso8601-timestamp = { version = "0.4.0", features = ["utoipa", "bson"] } # Spec Generation -schemars = { version = "0.8.8", optional = true, features = ["indexmap1"] } -utoipa = { version = "4.2.3", optional = true } +utoipa = { version = "5.4.0", features = ["indexmap"], optional = true } # Validation validator = { version = "0.16.0", optional = true, features = ["derive"] } diff --git a/crates/core/models/src/lib.rs b/crates/core/models/src/lib.rs index 7f3d3d23..511e0425 100644 --- a/crates/core/models/src/lib.rs +++ b/crates/core/models/src/lib.rs @@ -2,10 +2,6 @@ #[macro_use] extern crate serde; -#[cfg(feature = "schemas")] -#[macro_use] -extern crate schemars; - #[cfg(feature = "utoipa")] #[macro_use] extern crate utoipa; @@ -21,7 +17,6 @@ macro_rules! auto_derived { ( $( $item:item )+ ) => { $( #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] - #[cfg_attr(feature = "schemas", derive(JsonSchema))] #[cfg_attr(feature = "utoipa", derive(ToSchema))] #[derive(Debug, Clone, Eq, PartialEq)] $item @@ -32,19 +27,8 @@ macro_rules! auto_derived { #[cfg(feature = "partials")] macro_rules! auto_derived_partial { ( $item:item, $name:expr ) => { - #[derive( - OptionalStruct, Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema, - )] - #[optional_derive( - Debug, - Clone, - Eq, - PartialEq, - Serialize, - Deserialize, - JsonSchema, - Default - )] + #[derive(OptionalStruct, Debug, Clone, Eq, PartialEq, Serialize, Deserialize, ToSchema)] + #[optional_derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, ToSchema, Default)] #[optional_name = $name] #[opt_skip_serializing_none] #[opt_some_priority] @@ -55,7 +39,7 @@ macro_rules! auto_derived_partial { #[cfg(not(feature = "partials"))] macro_rules! auto_derived_partial { ( $item:item, $name:expr ) => { - #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, JsonSchema)] + #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, ToSchema)] $item }; } diff --git a/crates/core/models/src/v0/channel_webhooks.rs b/crates/core/models/src/v0/channel_webhooks.rs index 710152b5..92bcee8e 100644 --- a/crates/core/models/src/v0/channel_webhooks.rs +++ b/crates/core/models/src/v0/channel_webhooks.rs @@ -86,10 +86,10 @@ auto_derived!( /// Information for the webhook #[cfg_attr(feature = "validator", derive(Validate))] pub struct CreateWebhookBody { - #[validate(length(min = 1, max = 32))] + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))] pub name: String, - #[validate(length(min = 1, max = 128))] + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 128)))] pub avatar: Option, } ); diff --git a/crates/core/models/src/v0/config.rs b/crates/core/models/src/v0/config.rs new file mode 100644 index 00000000..ae5163f1 --- /dev/null +++ b/crates/core/models/src/v0/config.rs @@ -0,0 +1,73 @@ +auto_derived!( + /// # hCaptcha Configuration + pub struct CaptchaFeature { + /// Whether captcha is enabled + pub enabled: bool, + /// Client key used for solving captcha + pub key: String, + } + + /// # Generic Service Configuration + pub struct Feature { + /// Whether the service is enabled + pub enabled: bool, + /// URL pointing to the service + pub url: String, + } + + /// # Voice Server Configuration + pub struct VoiceFeature { + /// Whether voice is enabled + pub enabled: bool, + /// URL pointing to the voice API + pub url: String, + /// URL pointing to the voice WebSocket server + pub ws: String, + } + + /// # Feature Configuration + pub struct RevoltFeatures { + /// hCaptcha configuration + pub captcha: CaptchaFeature, + /// Whether email verification is enabled + pub email: bool, + /// Whether this server is invite only + pub invite_only: bool, + /// File server service configuration + pub autumn: Feature, + /// Proxy service configuration + pub january: Feature, + /// Voice server configuration + pub voso: VoiceFeature, + } + + /// # Build Information + pub struct BuildInformation { + /// Commit Hash + pub commit_sha: String, + /// Commit Timestamp + pub commit_timestamp: String, + /// Git Semver + pub semver: String, + /// Git Origin URL + pub origin_url: String, + /// Build Timestamp + pub timestamp: String, + } + + /// # Server Configuration + pub struct RevoltConfig { + /// Revolt API Version + pub revolt: String, + /// Features enabled on this Revolt node + pub features: RevoltFeatures, + /// WebSocket URL + pub ws: String, + /// URL pointing to the client serving this node + pub app: String, + /// Web Push VAPID public key + pub vapid: String, + /// Build information + pub build: BuildInformation, + } +); \ No newline at end of file diff --git a/crates/core/models/src/v0/emojis.rs b/crates/core/models/src/v0/emojis.rs index 2d7c015e..59c19cb5 100644 --- a/crates/core/models/src/v0/emojis.rs +++ b/crates/core/models/src/v0/emojis.rs @@ -46,7 +46,7 @@ auto_derived!( #[cfg_attr(feature = "validator", derive(Validate))] pub struct DataCreateEmoji { /// Server name - #[validate(length(min = 1, max = 32), regex = "RE_EMOJI")] + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 32), regex = "RE_EMOJI"))] pub name: String, /// Parent information pub parent: EmojiParent, diff --git a/crates/core/models/src/v0/messages.rs b/crates/core/models/src/v0/messages.rs index 2116d98b..dc10ff7e 100644 --- a/crates/core/models/src/v0/messages.rs +++ b/crates/core/models/src/v0/messages.rs @@ -139,17 +139,17 @@ auto_derived!( pub struct Masquerade { /// Replace the display name shown on this message #[serde(skip_serializing_if = "Option::is_none")] - #[validate(length(min = 1, max = 32))] + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 32)))] pub name: Option, /// Replace the avatar shown on this message (URL to image file) #[serde(skip_serializing_if = "Option::is_none")] - #[validate(length(min = 1, max = 256))] + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 256)))] pub avatar: Option, /// Replace the display role colour shown on this message /// /// Must have `ManageRole` permission to use #[serde(skip_serializing_if = "Option::is_none")] - #[validate(length(min = 1, max = 128), regex = "RE_COLOUR")] + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 128), regex = "RE_COLOUR"))] pub colour: Option, } @@ -351,7 +351,7 @@ auto_derived!( )] pub struct OptionsBulkDelete { /// Message IDs - #[validate(length(min = 1, max = 100))] + #[cfg_attr(feature = "validator", validate(length(min = 1, max = 100)))] pub ids: Vec, } diff --git a/crates/core/models/src/v0/mod.rs b/crates/core/models/src/v0/mod.rs index 0468493b..b582fbdb 100644 --- a/crates/core/models/src/v0/mod.rs +++ b/crates/core/models/src/v0/mod.rs @@ -3,6 +3,7 @@ mod channel_invites; mod channel_unreads; mod channel_webhooks; mod channels; +mod config; mod embeds; mod emojis; mod files; @@ -20,6 +21,7 @@ pub use channel_invites::*; pub use channel_unreads::*; pub use channel_webhooks::*; pub use channels::*; +pub use config::*; pub use embeds::*; pub use emojis::*; pub use files::*; diff --git a/crates/core/models/src/v0/users.rs b/crates/core/models/src/v0/users.rs index fc681e00..8558cdad 100644 --- a/crates/core/models/src/v0/users.rs +++ b/crates/core/models/src/v0/users.rs @@ -141,7 +141,7 @@ auto_derived!( #[cfg_attr(feature = "validator", derive(Validate))] pub struct UserStatus { /// Custom status text - #[validate(length(min = 0, max = 128))] + #[cfg_attr(feature = "validator", validate(length(min = 0, max = 128)))] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub text: Option, /// Current presence option @@ -154,7 +154,7 @@ auto_derived!( #[cfg_attr(feature = "validator", derive(Validate))] pub struct UserProfile { /// Text content on user's profile - #[validate(length(min = 0, max = 2000))] + #[cfg_attr(feature = "validator", validate(length(min = 0, max = 2000)))] #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] pub content: Option, /// Background visible on user's profile diff --git a/crates/core/permissions/Cargo.toml b/crates/core/permissions/Cargo.toml index 6fcc3828..638708ff 100644 --- a/crates/core/permissions/Cargo.toml +++ b/crates/core/permissions/Cargo.toml @@ -11,10 +11,8 @@ description = "Revolt Backend: Permission Logic" [features] bson = ["dep:bson"] serde = ["dep:serde"] -schemas = ["dep:schemars"] try-from-primitive = ["dep:num_enum"] - [dev-dependencies] # Async async-std = { version = "1.8.0", features = ["attributes"] } @@ -36,4 +34,4 @@ serde = { version = "1", features = ["derive"], optional = true } bson = { version = "2.1.0", optional = true } # Spec Generation -schemars = { version = "0.8.8", optional = true } +utoipa = { version = "5.4.0", optional = true } diff --git a/crates/core/permissions/src/models/server.rs b/crates/core/permissions/src/models/server.rs index e6355eed..5f417b94 100644 --- a/crates/core/permissions/src/models/server.rs +++ b/crates/core/permissions/src/models/server.rs @@ -1,10 +1,10 @@ -#[cfg(feature = "schemas")] -use schemars::JsonSchema; +#[cfg(feature = "utoipa")] +use utoipa::ToSchema; /// Representation of a single permission override #[derive(Debug, Clone, Eq, PartialEq, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "schemas", derive(JsonSchema))] +#[cfg_attr(feature = "utoipa", derive(ToSchema))] pub struct Override { /// Allow bit flags pub allow: u64, @@ -15,7 +15,7 @@ pub struct Override { /// Data permissions Field - contains both allow and deny #[derive(Debug, Clone, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "schemas", derive(JsonSchema))] +#[cfg_attr(feature = "utoipa", derive(ToSchema))] pub struct DataPermissionsField { pub permissions: Override, } @@ -23,7 +23,7 @@ pub struct DataPermissionsField { /// Data permissions Value - contains allow #[derive(Debug, Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "schemas", derive(JsonSchema))] +#[cfg_attr(feature = "utoipa", derive(ToSchema))] pub struct DataPermissionsValue { pub permissions: u64, } @@ -31,7 +31,7 @@ pub struct DataPermissionsValue { /// Data permissions Poly - can contain either Value or Field #[derive(Debug, Clone, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "schemas", derive(JsonSchema))] +#[cfg_attr(feature = "utoipa", derive(ToSchema))] #[cfg_attr(feature = "serde", serde(untagged))] pub enum DataPermissionPoly { Value { @@ -48,7 +48,7 @@ pub enum DataPermissionPoly { /// as it appears on models and in the database #[derive(Debug, Clone, Copy, Default, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[cfg_attr(feature = "schemas", derive(JsonSchema))] +#[cfg_attr(feature = "utoipa", derive(ToSchema))] pub struct OverrideField { /// Allow bit flags pub a: i64, diff --git a/crates/core/ratelimits/Cargo.toml b/crates/core/ratelimits/Cargo.toml index a41952bb..2d96aaa6 100644 --- a/crates/core/ratelimits/Cargo.toml +++ b/crates/core/ratelimits/Cargo.toml @@ -4,20 +4,24 @@ version = "0.8.9" edition = "2024" [features] -rocket = ["dep:rocket", "dep:revolt_rocket_okapi", "revolt-database/rocket-impl"] +rocket = [ + "dep:rocket", + "dep:revolt_rocket_okapi", + "revolt-database/rocket-impl", +] axum = ["dep:axum", "revolt-database/axum-impl"] default = ["rocket", "axum"] [dependencies] -revolt-database = { version = "0.8.9", path = "../database"} +revolt-database = { version = "0.8.9", path = "../database" } revolt-result = { version = "0.8.9", path = "../result" } revolt-config = { version = "0.8.9", path = "../config" } rocket = { version = "0.5.1", optional = true } revolt_rocket_okapi = { version = "0.10.0", optional = true } -axum = { version = "0.7.5", optional = true, features = ["macros"] } +axum = { version = "0.8.6", optional = true, features = ["macros"] } serde = { version = "1", features = ["derive"] } authifier = { version = "1.0.15" } diff --git a/crates/core/ratelimits/src/axum.rs b/crates/core/ratelimits/src/axum.rs index f50cf867..e4886f19 100644 --- a/crates/core/ratelimits/src/axum.rs +++ b/crates/core/ratelimits/src/axum.rs @@ -1,6 +1,5 @@ use std::net::SocketAddr; -use async_trait::async_trait; use axum::{ Json, RequestPartsExt, Router, body::Body, @@ -44,7 +43,6 @@ async fn to_real_ip(parts: &Parts) -> String { } } -#[async_trait] impl FromRequestParts for Ratelimiter where Database: FromRef, @@ -82,7 +80,6 @@ where } } -#[async_trait] impl FromRequestParts for RatelimitInformation where Database: FromRef, diff --git a/crates/core/result/Cargo.toml b/crates/core/result/Cargo.toml index b8ce0c59..dadbe568 100644 --- a/crates/core/result/Cargo.toml +++ b/crates/core/result/Cargo.toml @@ -10,11 +10,9 @@ description = "Revolt Backend: Result and Error types" [features] serde = ["dep:serde"] -schemas = ["dep:schemars"] utoipa = ["dep:utoipa"] rocket = ["dep:rocket", "dep:serde_json"] axum = ["dep:axum", "dep:serde_json"] -okapi = ["dep:revolt_rocket_okapi", "dep:revolt_okapi", "schemas"] default = ["serde"] @@ -24,13 +22,10 @@ serde_json = { version = "1", optional = true } serde = { version = "1", features = ["derive"], optional = true } # Spec Generation -schemars = { version = "0.8.8", optional = true } -utoipa = { version = "4.2.3", optional = true } +utoipa = { version = "5.4.0", optional = true } # Rocket rocket = { optional = true, version = "0.5.0-rc.2", default-features = false } -revolt_rocket_okapi = { version = "0.10.0", optional = true } -revolt_okapi = { version = "0.9.1", optional = true } # Axum -axum = { version = "0.7.5", optional = true } +axum = { version = "0.8.6", optional = true } diff --git a/crates/core/result/src/lib.rs b/crates/core/result/src/lib.rs index 5ae2ac2a..cafd564a 100644 --- a/crates/core/result/src/lib.rs +++ b/crates/core/result/src/lib.rs @@ -4,10 +4,6 @@ use std::fmt::Display; #[macro_use] extern crate serde; -#[cfg(feature = "schemas")] -#[macro_use] -extern crate schemars; - #[cfg(feature = "utoipa")] #[macro_use] extern crate utoipa; @@ -18,15 +14,11 @@ pub mod rocket; #[cfg(feature = "axum")] pub mod axum; -#[cfg(feature = "okapi")] -pub mod okapi; - /// Result type with custom Error pub type Result = std::result::Result; /// Error information #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "schemas", derive(JsonSchema))] #[cfg_attr(feature = "utoipa", derive(ToSchema))] #[derive(Debug, Clone)] pub struct Error { @@ -49,7 +41,6 @@ impl std::error::Error for Error {} /// Possible error types #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(tag = "type"))] -#[cfg_attr(feature = "schemas", derive(JsonSchema))] #[cfg_attr(feature = "utoipa", derive(ToSchema))] #[derive(Debug, Clone)] pub enum ErrorType { diff --git a/crates/core/result/src/okapi.rs b/crates/core/result/src/okapi.rs deleted file mode 100644 index 0d6e724f..00000000 --- a/crates/core/result/src/okapi.rs +++ /dev/null @@ -1,49 +0,0 @@ -use revolt_okapi::openapi3::SchemaObject; -use revolt_rocket_okapi::revolt_okapi::openapi3; -use schemars::schema::Schema; - -use crate::Error; - -impl revolt_rocket_okapi::response::OpenApiResponderInner for Error { - fn responses( - gen: &mut revolt_rocket_okapi::gen::OpenApiGenerator, - ) -> std::result::Result { - let mut content = revolt_okapi::Map::new(); - - let settings = schemars::gen::SchemaSettings::default().with(|s| { - s.option_nullable = true; - s.option_add_null_type = false; - s.definitions_path = "#/components/schemas/".to_string(); - }); - - let mut schema_generator = settings.into_generator(); - let schema = schema_generator.root_schema_for::(); - - let definitions = gen.schema_generator().definitions_mut(); - for (key, value) in schema.definitions { - definitions.insert(key, value); - } - - definitions.insert("Error".to_string(), Schema::Object(schema.schema)); - - content.insert( - "application/json".to_string(), - openapi3::MediaType { - schema: Some(SchemaObject { - reference: Some("#/components/schemas/Error".to_string()), - ..Default::default() - }), - ..Default::default() - }, - ); - - Ok(openapi3::Responses { - default: Some(openapi3::RefOr::Object(openapi3::Response { - content, - description: "An error occurred.".to_string(), - ..Default::default() - })), - ..Default::default() - }) - } -} diff --git a/crates/daemons/pushd/Cargo.toml b/crates/daemons/pushd/Cargo.toml index 71fc002b..f7ddf370 100644 --- a/crates/daemons/pushd/Cargo.toml +++ b/crates/daemons/pushd/Cargo.toml @@ -38,5 +38,5 @@ pretty_env_logger = "0.4.0" serde_json = "1" revolt_optional_struct = "0.2.0" serde = { version = "1", features = ["derive"] } -iso8601-timestamp = { version = "0.2.10", features = ["serde", "bson"] } +iso8601-timestamp = { version = "0.4.0", features = ["serde", "bson"] } base64 = "0.22.1" diff --git a/crates/delta/Cargo.toml b/crates/delta/Cargo.toml index fa4bc806..6dd3ad51 100644 --- a/crates/delta/Cargo.toml +++ b/crates/delta/Cargo.toml @@ -35,7 +35,7 @@ nanoid = "0.4.0" serde_json = "1.0.57" serde = { version = "1.0.115", features = ["derive"] } validator = { version = "0.16", features = ["derive"] } -iso8601-timestamp = { version = "0.2.11", features = [] } +iso8601-timestamp = { version = "0.4.0", features = [] } # async futures = "0.3.8" @@ -60,8 +60,8 @@ rocket_authifier = { version = "1.0.15" } rocket_prometheus = "0.10.0-rc.3" # spec generation -schemars = "0.8.8" -revolt_rocket_okapi = { version = "0.10.0", features = ["swagger"] } +utoipa = { version = "5.4.0", features = ["rocket_extras"] } +utoipa-scalar = { version = "0.3.0", features = ["rocket"] } # rabbit amqprs = { version = "1.7.0" } @@ -72,15 +72,16 @@ revolt-config = { path = "../core/config" } revolt-database = { path = "../core/database", features = [ "rocket-impl", "redis-is-patched", + "utoipa" ] } revolt-models = { path = "../core/models", features = [ - "schemas", + "utoipa", "validator", "rocket", ] } revolt-presence = { path = "../core/presence" } -revolt-result = { path = "../core/result", features = ["rocket", "okapi"] } -revolt-permissions = { path = "../core/permissions", features = ["schemas"] } +revolt-result = { path = "../core/result", features = ["rocket", "utoipa"] } +revolt-permissions = { path = "../core/permissions", features = ["utoipa"] } revolt-ratelimits = { path = "../core/ratelimits", features = ["rocket"] } [build-dependencies] diff --git a/crates/delta/src/main.rs b/crates/delta/src/main.rs index 6d0eaa60..c958e815 100644 --- a/crates/delta/src/main.rs +++ b/crates/delta/src/main.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate rocket; #[macro_use] -extern crate revolt_rocket_okapi; +extern crate utoipa; #[macro_use] extern crate serde_json; @@ -15,6 +15,8 @@ use revolt_ratelimits::rocket as ratelimiter; use rocket::{Build, Rocket}; use rocket_cors::{AllowedOrigins, CorsOptions}; use rocket_prometheus::PrometheusMetrics; +use utoipa::OpenApi; +use utoipa_scalar::{Scalar, Servable}; use std::net::Ipv4Addr; use std::str::FromStr; @@ -73,22 +75,6 @@ pub async fn web() -> Rocket { .to_cors() .expect("Failed to create CORS."); - // Configure Swagger - let swagger = revolt_rocket_okapi::swagger_ui::make_swagger_ui( - &revolt_rocket_okapi::swagger_ui::SwaggerUIConfig { - url: "/openapi.json".to_owned(), - ..Default::default() - }, - ) - .into(); - - let swagger_0_8 = revolt_rocket_okapi::swagger_ui::make_swagger_ui( - &revolt_rocket_okapi::swagger_ui::SwaggerUIConfig { - url: "/0.8/openapi.json".to_owned(), - ..Default::default() - }, - ) - .into(); // Configure Rabbit let connection = Connection::open(&OpenConnectionArguments::new( @@ -131,8 +117,7 @@ pub async fn web() -> Rocket { .mount("/metrics", prometheus) .mount("/", rocket_cors::catch_all_options_routes()) .mount("/", ratelimiter::routes()) - .mount("/swagger/", swagger) - .mount("/0.8/swagger/", swagger_0_8) + .mount("/", Scalar::with_url("/scalar", routes::ApiDoc::openapi())) .manage(authifier) .manage(db) .manage(amqp) diff --git a/crates/delta/src/routes/bots/create.rs b/crates/delta/src/routes/bots/create.rs index 67a97f07..6f05c24f 100644 --- a/crates/delta/src/routes/bots/create.rs +++ b/crates/delta/src/routes/bots/create.rs @@ -8,7 +8,7 @@ use validator::Validate; /// # Create Bot /// /// Create a new Revolt bot. -#[openapi(tag = "Bots")] +#[utoipa::path(tag = "Bots")] #[post("/create", data = "")] pub async fn create_bot( db: &State, diff --git a/crates/delta/src/routes/bots/delete.rs b/crates/delta/src/routes/bots/delete.rs index 4d4daefb..bad58f6a 100644 --- a/crates/delta/src/routes/bots/delete.rs +++ b/crates/delta/src/routes/bots/delete.rs @@ -6,7 +6,7 @@ use rocket_empty::EmptyResponse; /// # Delete Bot /// /// Delete a bot by its id. -#[openapi(tag = "Bots")] +#[utoipa::path(tag = "Bots")] #[delete("/")] pub async fn delete_bot( db: &State, diff --git a/crates/delta/src/routes/bots/edit.rs b/crates/delta/src/routes/bots/edit.rs index 6c454e13..31fef5d5 100644 --- a/crates/delta/src/routes/bots/edit.rs +++ b/crates/delta/src/routes/bots/edit.rs @@ -9,7 +9,7 @@ use validator::Validate; /// # Edit Bot /// /// Edit bot details by its id. -#[openapi(tag = "Bots")] +#[utoipa::path(tag = "Bots")] #[patch("/", data = "")] pub async fn edit_bot( db: &State, @@ -60,15 +60,8 @@ pub async fn edit_bot( ..Default::default() }; - bot.update( - db, - partial, - remove - .into_iter() - .map(|v| v.into()) - .collect(), - ) - .await?; + bot.update(db, partial, remove.into_iter().map(|v| v.into()).collect()) + .await?; Ok(Json(v0::BotWithUserResponse { bot: bot.into(), diff --git a/crates/delta/src/routes/bots/fetch.rs b/crates/delta/src/routes/bots/fetch.rs index 586ce124..87fa6737 100644 --- a/crates/delta/src/routes/bots/fetch.rs +++ b/crates/delta/src/routes/bots/fetch.rs @@ -6,7 +6,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch Bot /// /// Fetch details of a bot you own by its id. -#[openapi(tag = "Bots")] +#[utoipa::path(tag = "Bots")] #[get("/")] pub async fn fetch_bot( db: &State, diff --git a/crates/delta/src/routes/bots/fetch_owned.rs b/crates/delta/src/routes/bots/fetch_owned.rs index 9eb320ef..dfb50cea 100644 --- a/crates/delta/src/routes/bots/fetch_owned.rs +++ b/crates/delta/src/routes/bots/fetch_owned.rs @@ -8,7 +8,7 @@ use rocket::State; /// # Fetch Owned Bots /// /// Fetch all of the bots that you have control over. -#[openapi(tag = "Bots")] +#[utoipa::path(tag = "Bots")] #[get("/@me")] pub async fn fetch_owned_bots(db: &State, user: User) -> Result> { let mut bots = db.fetch_bots_by_user(&user.id).await?; diff --git a/crates/delta/src/routes/bots/fetch_public.rs b/crates/delta/src/routes/bots/fetch_public.rs index 7ad97e00..804ff3a5 100644 --- a/crates/delta/src/routes/bots/fetch_public.rs +++ b/crates/delta/src/routes/bots/fetch_public.rs @@ -8,7 +8,7 @@ use rocket::State; /// # Fetch Public Bot /// /// Fetch details of a public (or owned) bot by its id. -#[openapi(tag = "Bots")] +#[utoipa::path(tag = "Bots")] #[get("//invite")] pub async fn fetch_public_bot( db: &State, diff --git a/crates/delta/src/routes/bots/invite.rs b/crates/delta/src/routes/bots/invite.rs index ad4f16ad..ce0cc70d 100644 --- a/crates/delta/src/routes/bots/invite.rs +++ b/crates/delta/src/routes/bots/invite.rs @@ -14,7 +14,7 @@ use rocket_empty::EmptyResponse; /// # Invite Bot /// /// Invite a bot to a server or group by its id.` -#[openapi(tag = "Bots")] +#[utoipa::path(tag = "Bots")] #[post("//invite", data = "")] pub async fn invite_bot( db: &State, diff --git a/crates/delta/src/routes/bots/mod.rs b/crates/delta/src/routes/bots/mod.rs index 0ae1a954..c1736120 100644 --- a/crates/delta/src/routes/bots/mod.rs +++ b/crates/delta/src/routes/bots/mod.rs @@ -1,4 +1,3 @@ -use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; use rocket::Route; mod create; @@ -9,8 +8,22 @@ mod fetch_owned; mod fetch_public; mod invite; -pub fn routes() -> (Vec, OpenApi) { - openapi_get_routes_spec![ +#[derive(OpenApi)] +#[openapi( + paths( + create::create_bot, + invite::invite_bot, + fetch_public::fetch_public_bot, + fetch::fetch_bot, + fetch_owned::fetch_owned_bots, + edit::edit_bot, + delete::delete_bot, + ) +)] +pub struct ApiDoc; + +pub fn routes() -> Vec { + routes![ create::create_bot, invite::invite_bot, fetch_public::fetch_public_bot, diff --git a/crates/delta/src/routes/channels/channel_ack.rs b/crates/delta/src/routes/channels/channel_ack.rs index 2ae0d3b0..3de322d9 100644 --- a/crates/delta/src/routes/channels/channel_ack.rs +++ b/crates/delta/src/routes/channels/channel_ack.rs @@ -10,7 +10,7 @@ use rocket_empty::EmptyResponse; /// # Acknowledge Message /// /// Lets the server and all other clients know that we've seen this message id in this channel. -#[openapi(tag = "Messaging")] +#[utoipa::path(tag = "Messaging")] #[put("//ack/")] pub async fn ack( db: &State, diff --git a/crates/delta/src/routes/channels/channel_delete.rs b/crates/delta/src/routes/channels/channel_delete.rs index 9d2b37b8..bf32fecd 100644 --- a/crates/delta/src/routes/channels/channel_delete.rs +++ b/crates/delta/src/routes/channels/channel_delete.rs @@ -11,7 +11,7 @@ use rocket_empty::EmptyResponse; /// # Close Channel /// /// Deletes a server channel, leaves a group or closes a group. -#[openapi(tag = "Channel Information")] +#[utoipa::path(tag = "Channel Information")] #[delete("/?")] pub async fn delete( db: &State, diff --git a/crates/delta/src/routes/channels/channel_edit.rs b/crates/delta/src/routes/channels/channel_edit.rs index 881656f6..d331d06b 100644 --- a/crates/delta/src/routes/channels/channel_edit.rs +++ b/crates/delta/src/routes/channels/channel_edit.rs @@ -11,7 +11,7 @@ use validator::Validate; /// # Edit Channel /// /// Edit a channel object by its id. -#[openapi(tag = "Channel Information")] +#[utoipa::path(tag = "Channel Information")] #[patch("/", data = "")] pub async fn edit( db: &State, diff --git a/crates/delta/src/routes/channels/channel_fetch.rs b/crates/delta/src/routes/channels/channel_fetch.rs index b08239bb..5a2e40f8 100644 --- a/crates/delta/src/routes/channels/channel_fetch.rs +++ b/crates/delta/src/routes/channels/channel_fetch.rs @@ -11,7 +11,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch Channel /// /// Fetch channel by its id. -#[openapi(tag = "Channel Information")] +#[utoipa::path(tag = "Channel Information")] #[get("/")] pub async fn fetch( db: &State, diff --git a/crates/delta/src/routes/channels/group_add_member.rs b/crates/delta/src/routes/channels/group_add_member.rs index ac1aa886..2e88a9fe 100644 --- a/crates/delta/src/routes/channels/group_add_member.rs +++ b/crates/delta/src/routes/channels/group_add_member.rs @@ -11,7 +11,7 @@ use rocket_empty::EmptyResponse; /// # Add Member to Group /// /// Adds another user to the group. -#[openapi(tag = "Groups")] +#[utoipa::path(tag = "Groups")] #[put("//recipients/")] pub async fn add_member( db: &State, diff --git a/crates/delta/src/routes/channels/group_create.rs b/crates/delta/src/routes/channels/group_create.rs index bf6387c5..6ac6cd53 100644 --- a/crates/delta/src/routes/channels/group_create.rs +++ b/crates/delta/src/routes/channels/group_create.rs @@ -9,7 +9,7 @@ use validator::Validate; /// # Create Group /// /// Create a new group channel. -#[openapi(tag = "Groups")] +#[utoipa::path(tag = "Groups")] #[post("/create", data = "")] pub async fn create_group( db: &State, diff --git a/crates/delta/src/routes/channels/group_remove_member.rs b/crates/delta/src/routes/channels/group_remove_member.rs index 467168ba..3d9e0f32 100644 --- a/crates/delta/src/routes/channels/group_remove_member.rs +++ b/crates/delta/src/routes/channels/group_remove_member.rs @@ -8,7 +8,7 @@ use rocket_empty::EmptyResponse; /// # Remove Member from Group /// /// Removes a user from the group. -#[openapi(tag = "Groups")] +#[utoipa::path(tag = "Groups")] #[delete("//recipients/")] pub async fn remove_member( db: &State, diff --git a/crates/delta/src/routes/channels/invite_create.rs b/crates/delta/src/routes/channels/invite_create.rs index 104f3398..dd6734e2 100644 --- a/crates/delta/src/routes/channels/invite_create.rs +++ b/crates/delta/src/routes/channels/invite_create.rs @@ -13,7 +13,7 @@ use rocket::{serde::json::Json, State}; /// Creates an invite to this channel. /// /// Channel must be a `TextChannel`. -#[openapi(tag = "Channel Invites")] +#[utoipa::path(tag = "Channel Invites")] #[post("//invites")] pub async fn create_invite( db: &State, diff --git a/crates/delta/src/routes/channels/members_fetch.rs b/crates/delta/src/routes/channels/members_fetch.rs index dc681519..42b5b48d 100644 --- a/crates/delta/src/routes/channels/members_fetch.rs +++ b/crates/delta/src/routes/channels/members_fetch.rs @@ -12,7 +12,7 @@ use rocket::{serde::json::Json, State}; /// Retrieves all users who are part of this group. /// /// This may not return full user information if users are not friends but have mutual connections. -#[openapi(tag = "Groups")] +#[utoipa::path(tag = "Groups")] #[get("//members")] pub async fn fetch_members( db: &State, diff --git a/crates/delta/src/routes/channels/message_bulk_delete.rs b/crates/delta/src/routes/channels/message_bulk_delete.rs index fdd538ff..12b6b1e4 100644 --- a/crates/delta/src/routes/channels/message_bulk_delete.rs +++ b/crates/delta/src/routes/channels/message_bulk_delete.rs @@ -17,7 +17,7 @@ use validator::Validate; /// This will always require `ManageMessages` permission regardless of whether you own the message or not. /// /// Messages must have been sent within the past 1 week. -#[openapi(tag = "Messaging")] +#[utoipa::path(tag = "Messaging")] #[delete("//messages/bulk", data = "", rank = 1)] pub async fn bulk_delete_messages( db: &State, diff --git a/crates/delta/src/routes/channels/message_clear_reactions.rs b/crates/delta/src/routes/channels/message_clear_reactions.rs index cf9be252..67a5841d 100644 --- a/crates/delta/src/routes/channels/message_clear_reactions.rs +++ b/crates/delta/src/routes/channels/message_clear_reactions.rs @@ -12,7 +12,7 @@ use rocket_empty::EmptyResponse; /// Remove your own, someone else's or all of a given reaction. /// /// Requires `ManageMessages` permission. -#[openapi(tag = "Interactions")] +#[utoipa::path(tag = "Interactions")] #[delete("//messages//reactions")] pub async fn clear_reactions( db: &State, diff --git a/crates/delta/src/routes/channels/message_delete.rs b/crates/delta/src/routes/channels/message_delete.rs index 437c80ee..7ca6c69b 100644 --- a/crates/delta/src/routes/channels/message_delete.rs +++ b/crates/delta/src/routes/channels/message_delete.rs @@ -10,7 +10,7 @@ use rocket_empty::EmptyResponse; /// # Delete Message /// /// Delete a message you've sent or one you have permission to delete. -#[openapi(tag = "Messaging")] +#[utoipa::path(tag = "Messaging")] #[delete("//messages/", rank = 2)] pub async fn delete( db: &State, diff --git a/crates/delta/src/routes/channels/message_edit.rs b/crates/delta/src/routes/channels/message_edit.rs index 53426ec0..201ee3ff 100644 --- a/crates/delta/src/routes/channels/message_edit.rs +++ b/crates/delta/src/routes/channels/message_edit.rs @@ -13,7 +13,7 @@ use validator::Validate; /// # Edit Message /// /// Edits a message that you've previously sent. -#[openapi(tag = "Messaging")] +#[utoipa::path(tag = "Messaging")] #[patch("//messages/", data = "")] pub async fn edit( db: &State, diff --git a/crates/delta/src/routes/channels/message_fetch.rs b/crates/delta/src/routes/channels/message_fetch.rs index 8f09d47f..98f917d6 100644 --- a/crates/delta/src/routes/channels/message_fetch.rs +++ b/crates/delta/src/routes/channels/message_fetch.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch Message /// /// Retrieves a message by its id. -#[openapi(tag = "Messaging")] +#[utoipa::path(tag = "Messaging")] #[get("//messages/")] pub async fn fetch( db: &State, diff --git a/crates/delta/src/routes/channels/message_pin.rs b/crates/delta/src/routes/channels/message_pin.rs index bcc97341..2937d5ce 100644 --- a/crates/delta/src/routes/channels/message_pin.rs +++ b/crates/delta/src/routes/channels/message_pin.rs @@ -11,7 +11,7 @@ use rocket_empty::EmptyResponse; /// # Pins a message /// /// Pins a message by its id. -#[openapi(tag = "Messaging")] +#[utoipa::path(tag = "Messaging")] #[post("//messages//pin")] pub async fn message_pin( db: &State, diff --git a/crates/delta/src/routes/channels/message_query.rs b/crates/delta/src/routes/channels/message_query.rs index 27df85e4..d40081c6 100644 --- a/crates/delta/src/routes/channels/message_query.rs +++ b/crates/delta/src/routes/channels/message_query.rs @@ -11,7 +11,7 @@ use validator::Validate; /// # Fetch Messages /// /// Fetch multiple messages. -#[openapi(tag = "Messaging")] +#[utoipa::path(tag = "Messaging")] #[get("//messages?")] pub async fn query( db: &State, diff --git a/crates/delta/src/routes/channels/message_react.rs b/crates/delta/src/routes/channels/message_react.rs index 6d3edb2a..9cd1d9b2 100644 --- a/crates/delta/src/routes/channels/message_react.rs +++ b/crates/delta/src/routes/channels/message_react.rs @@ -10,7 +10,7 @@ use rocket_empty::EmptyResponse; /// # Add Reaction to Message /// /// React to a given message. -#[openapi(tag = "Interactions")] +#[utoipa::path(tag = "Interactions")] #[put("//messages//reactions/")] pub async fn react_message( db: &State, diff --git a/crates/delta/src/routes/channels/message_search.rs b/crates/delta/src/routes/channels/message_search.rs index 85676823..00617eef 100644 --- a/crates/delta/src/routes/channels/message_search.rs +++ b/crates/delta/src/routes/channels/message_search.rs @@ -11,7 +11,7 @@ use validator::Validate; /// # Search for Messages /// /// This route searches for messages within the given parameters. -#[openapi(tag = "Messaging")] +#[utoipa::path(tag = "Messaging")] #[post("//search", data = "")] pub async fn search( db: &State, diff --git a/crates/delta/src/routes/channels/message_send.rs b/crates/delta/src/routes/channels/message_send.rs index cb05f94b..cbcecbd9 100644 --- a/crates/delta/src/routes/channels/message_send.rs +++ b/crates/delta/src/routes/channels/message_send.rs @@ -15,7 +15,7 @@ use validator::Validate; /// # Send Message /// /// Sends a message to the given channel. -#[openapi(tag = "Messaging")] +#[utoipa::path(tag = "Messaging")] #[post("//messages", data = "")] pub async fn message_send( db: &State, diff --git a/crates/delta/src/routes/channels/message_unpin.rs b/crates/delta/src/routes/channels/message_unpin.rs index 3770606d..3e83e407 100644 --- a/crates/delta/src/routes/channels/message_unpin.rs +++ b/crates/delta/src/routes/channels/message_unpin.rs @@ -8,7 +8,7 @@ use rocket_empty::EmptyResponse; /// # Unpins a message /// /// Unpins a message by its id. -#[openapi(tag = "Messaging")] +#[utoipa::path(tag = "Messaging")] #[delete("//messages//pin")] pub async fn message_unpin( db: &State, diff --git a/crates/delta/src/routes/channels/message_unreact.rs b/crates/delta/src/routes/channels/message_unreact.rs index 285a1c43..84e7b3cb 100644 --- a/crates/delta/src/routes/channels/message_unreact.rs +++ b/crates/delta/src/routes/channels/message_unreact.rs @@ -13,7 +13,7 @@ use rocket_empty::EmptyResponse; /// Remove your own, someone else's or all of a given reaction. /// /// Requires `ManageMessages` if changing others' reactions. -#[openapi(tag = "Interactions")] +#[utoipa::path(tag = "Interactions")] #[delete("//messages//reactions/?")] pub async fn unreact_message( db: &State, diff --git a/crates/delta/src/routes/channels/mod.rs b/crates/delta/src/routes/channels/mod.rs index c57fbaa8..cfe7cb8c 100644 --- a/crates/delta/src/routes/channels/mod.rs +++ b/crates/delta/src/routes/channels/mod.rs @@ -1,4 +1,3 @@ -use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; use rocket::Route; mod channel_ack; @@ -28,8 +27,41 @@ mod voice_join; mod webhook_create; mod webhook_fetch_all; -pub fn routes() -> (Vec, OpenApi) { - openapi_get_routes_spec![ +#[derive(OpenApi)] +#[openapi( + paths( + channel_ack::ack, + channel_fetch::fetch, + members_fetch::fetch_members, + channel_delete::delete, + channel_edit::edit, + invite_create::create_invite, + message_send::message_send, + message_query::query, + message_search::search, + message_pin::message_pin, + message_fetch::fetch, + message_edit::edit, + message_bulk_delete::bulk_delete_messages, + message_delete::delete, + message_unpin::message_unpin, + group_create::create_group, + group_add_member::add_member, + group_remove_member::remove_member, + voice_join::call, + permissions_set::set_role_permissions, + permissions_set_default::set_default_channel_permissions, + message_react::react_message, + message_unreact::unreact_message, + message_clear_reactions::clear_reactions, + webhook_create::create_webhook, + webhook_fetch_all::fetch_webhooks, + ) +)] +pub struct ApiDoc; + +pub fn routes() -> Vec { + routes![ channel_ack::ack, channel_fetch::fetch, members_fetch::fetch_members, diff --git a/crates/delta/src/routes/channels/permissions_set.rs b/crates/delta/src/routes/channels/permissions_set.rs index ee42e403..59dbb153 100644 --- a/crates/delta/src/routes/channels/permissions_set.rs +++ b/crates/delta/src/routes/channels/permissions_set.rs @@ -12,7 +12,7 @@ use rocket::{serde::json::Json, State}; /// Sets permissions for the specified role in this channel. /// /// Channel must be a `TextChannel` or `VoiceChannel`. -#[openapi(tag = "Channel Permissions")] +#[utoipa::path(tag = "Channel Permissions")] #[put("//permissions/", data = "", rank = 2)] pub async fn set_role_permissions( db: &State, diff --git a/crates/delta/src/routes/channels/permissions_set_default.rs b/crates/delta/src/routes/channels/permissions_set_default.rs index 700b29e4..6fea6d4a 100644 --- a/crates/delta/src/routes/channels/permissions_set_default.rs +++ b/crates/delta/src/routes/channels/permissions_set_default.rs @@ -12,7 +12,7 @@ use rocket::{serde::json::Json, State}; /// Sets permissions for the default role in this channel. /// /// Channel must be a `Group`, `TextChannel` or `VoiceChannel`. -#[openapi(tag = "Channel Permissions")] +#[utoipa::path(tag = "Channel Permissions")] #[put("//permissions/default", data = "", rank = 1)] pub async fn set_default_channel_permissions( db: &State, diff --git a/crates/delta/src/routes/channels/voice_join.rs b/crates/delta/src/routes/channels/voice_join.rs index c04e0011..c909f892 100644 --- a/crates/delta/src/routes/channels/voice_join.rs +++ b/crates/delta/src/routes/channels/voice_join.rs @@ -11,7 +11,7 @@ use rocket::{serde::json::Json, State}; /// # Join Call /// /// Asks the voice server for a token to join the call. -#[openapi(tag = "Voice")] +#[utoipa::path(tag = "Voice")] #[post("//join_call")] pub async fn call( db: &State, diff --git a/crates/delta/src/routes/channels/webhook_create.rs b/crates/delta/src/routes/channels/webhook_create.rs index 71f8e8de..f7e7c03c 100644 --- a/crates/delta/src/routes/channels/webhook_create.rs +++ b/crates/delta/src/routes/channels/webhook_create.rs @@ -14,7 +14,7 @@ use validator::Validate; /// # Creates a webhook /// /// Creates a webhook which 3rd party platforms can use to send messages -#[openapi(tag = "Webhooks")] +#[utoipa::path(tag = "Webhooks")] #[post("//webhooks", data = "")] pub async fn create_webhook( db: &State, diff --git a/crates/delta/src/routes/channels/webhook_fetch_all.rs b/crates/delta/src/routes/channels/webhook_fetch_all.rs index 4c4fdfd8..005e0da5 100644 --- a/crates/delta/src/routes/channels/webhook_fetch_all.rs +++ b/crates/delta/src/routes/channels/webhook_fetch_all.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; /// # Gets all webhooks /// /// Gets all webhooks inside the channel -#[openapi(tag = "Webhooks")] +#[utoipa::path(tag = "Webhooks")] #[get("//webhooks")] pub async fn fetch_webhooks( db: &State, diff --git a/crates/delta/src/routes/customisation/emoji_create.rs b/crates/delta/src/routes/customisation/emoji_create.rs index d8288dae..48183b3b 100644 --- a/crates/delta/src/routes/customisation/emoji_create.rs +++ b/crates/delta/src/routes/customisation/emoji_create.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; /// # Create New Emoji /// /// Create an emoji by its Autumn upload id. -#[openapi(tag = "Emojis")] +#[utoipa::path(tag = "Emojis")] #[put("/emoji/", data = "")] pub async fn create_emoji( db: &State, diff --git a/crates/delta/src/routes/customisation/emoji_delete.rs b/crates/delta/src/routes/customisation/emoji_delete.rs index 155d730d..f9fc6e84 100644 --- a/crates/delta/src/routes/customisation/emoji_delete.rs +++ b/crates/delta/src/routes/customisation/emoji_delete.rs @@ -11,7 +11,7 @@ use rocket_empty::EmptyResponse; /// # Delete Emoji /// /// Delete an emoji by its id. -#[openapi(tag = "Emojis")] +#[utoipa::path(tag = "Emojis")] #[delete("/emoji/")] pub async fn delete_emoji( db: &State, diff --git a/crates/delta/src/routes/customisation/emoji_fetch.rs b/crates/delta/src/routes/customisation/emoji_fetch.rs index cdc9aaed..297c2abf 100644 --- a/crates/delta/src/routes/customisation/emoji_fetch.rs +++ b/crates/delta/src/routes/customisation/emoji_fetch.rs @@ -7,7 +7,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch Emoji /// /// Fetch an emoji by its id. -#[openapi(tag = "Emojis")] +#[utoipa::path(tag = "Emojis")] #[get("/emoji/")] pub async fn fetch_emoji(db: &State, emoji_id: Reference<'_>) -> Result> { emoji_id diff --git a/crates/delta/src/routes/customisation/mod.rs b/crates/delta/src/routes/customisation/mod.rs index 56d40995..58f57139 100644 --- a/crates/delta/src/routes/customisation/mod.rs +++ b/crates/delta/src/routes/customisation/mod.rs @@ -1,12 +1,21 @@ -use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; use rocket::Route; mod emoji_create; mod emoji_delete; mod emoji_fetch; -pub fn routes() -> (Vec, OpenApi) { - openapi_get_routes_spec![ +#[derive(OpenApi)] +#[openapi( + paths( + emoji_create::create_emoji, + emoji_delete::delete_emoji, + emoji_fetch::fetch_emoji + ) +)] +pub struct ApiDoc; + +pub fn routes() -> Vec { + routes![ emoji_create::create_emoji, emoji_delete::delete_emoji, emoji_fetch::fetch_emoji diff --git a/crates/delta/src/routes/invites/invite_delete.rs b/crates/delta/src/routes/invites/invite_delete.rs index cbdc5db5..de2a4718 100644 --- a/crates/delta/src/routes/invites/invite_delete.rs +++ b/crates/delta/src/routes/invites/invite_delete.rs @@ -10,7 +10,7 @@ use rocket_empty::EmptyResponse; /// # Delete Invite /// /// Delete an invite by its id. -#[openapi(tag = "Invites")] +#[utoipa::path(tag = "Invites")] #[delete("/")] pub async fn delete(db: &State, user: User, target: Reference<'_>) -> Result { let invite = target.as_invite(db).await?; diff --git a/crates/delta/src/routes/invites/invite_fetch.rs b/crates/delta/src/routes/invites/invite_fetch.rs index 7c44d4ae..053b07ee 100644 --- a/crates/delta/src/routes/invites/invite_fetch.rs +++ b/crates/delta/src/routes/invites/invite_fetch.rs @@ -6,7 +6,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch Invite /// /// Fetch an invite by its id. -#[openapi(tag = "Invites")] +#[utoipa::path(tag = "Invites")] #[get("/")] pub async fn fetch(db: &State, target: Reference<'_>) -> Result> { Ok(Json(match target.as_invite(db).await? { diff --git a/crates/delta/src/routes/invites/invite_join.rs b/crates/delta/src/routes/invites/invite_join.rs index a18213f3..16bc4518 100644 --- a/crates/delta/src/routes/invites/invite_join.rs +++ b/crates/delta/src/routes/invites/invite_join.rs @@ -6,7 +6,7 @@ use rocket::{serde::json::Json, State}; /// # Join Invite /// /// Join an invite by its ID -#[openapi(tag = "Invites")] +#[utoipa::path(tag = "Invites")] #[post("/")] pub async fn join( db: &State, diff --git a/crates/delta/src/routes/invites/mod.rs b/crates/delta/src/routes/invites/mod.rs index 00e6fa39..fc26f103 100644 --- a/crates/delta/src/routes/invites/mod.rs +++ b/crates/delta/src/routes/invites/mod.rs @@ -1,12 +1,22 @@ -use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; use rocket::Route; mod invite_delete; mod invite_fetch; mod invite_join; -pub fn routes() -> (Vec, OpenApi) { - openapi_get_routes_spec![ +#[derive(OpenApi)] +#[openapi( + paths( + invite_fetch::fetch, + invite_join::join, + invite_delete::delete + ) +)] +pub struct ApiDoc; + + +pub fn routes() -> Vec { + routes![ invite_fetch::fetch, invite_join::join, invite_delete::delete diff --git a/crates/delta/src/routes/mod.rs b/crates/delta/src/routes/mod.rs index 9643a365..dc213701 100644 --- a/crates/delta/src/routes/mod.rs +++ b/crates/delta/src/routes/mod.rs @@ -1,8 +1,13 @@ use revolt_config::Settings; -use revolt_rocket_okapi::{revolt_okapi::openapi3::OpenApi, settings::OpenApiSettings}; +use revolt_database::{util::utoipa::TokenSecurity}; pub use rocket::http::Status; pub use rocket::response::Redirect; use rocket::{Build, Rocket}; +use rocket_authifier::SecurityAddon; +use utoipa::{ + openapi::{extensions::ExtensionsBuilder, OpenApi}, + Modify, +}; mod bots; mod channels; @@ -19,358 +24,311 @@ mod users; mod webhooks; pub fn mount(config: Settings, mut rocket: Rocket) -> Rocket { - let settings = OpenApiSettings::default(); + rocket = rocket + .mount("/", routes![root::root]) + .mount("/users", users::routes()) + .mount("/bots", bots::routes()) + .mount("/channels", channels::routes()) + .mount("/servers", servers::routes()) + .mount("/invites", invites::routes()) + .mount("/custom", customisation::routes()) + .mount("/safety", safety::routes()) + .mount("/auth/account", rocket_authifier::routes::account::routes()) + .mount("/auth/session", rocket_authifier::routes::session::routes()) + .mount("/auth/mfa", rocket_authifier::routes::mfa::routes()) + .mount("/onboard", onboard::routes()) + .mount("/policy", policy::routes()) + .mount("/push", push::routes()) + .mount("/sync", sync::routes()); if config.features.webhooks_enabled { - mount_endpoints_and_merged_docs! { - rocket, "/".to_owned(), settings, - "/" => (vec![], custom_openapi_spec()), - "" => openapi_get_routes_spec![root::root], - "/users" => users::routes(), - "/bots" => bots::routes(), - "/channels" => channels::routes(), - "/servers" => servers::routes(), - "/invites" => invites::routes(), - "/custom" => customisation::routes(), - "/safety" => safety::routes(), - "/auth/account" => rocket_authifier::routes::account::routes(), - "/auth/session" => rocket_authifier::routes::session::routes(), - "/auth/mfa" => rocket_authifier::routes::mfa::routes(), - "/onboard" => onboard::routes(), - "/policy" => policy::routes(), - "/push" => push::routes(), - "/sync" => sync::routes(), - "/webhooks" => webhooks::routes() - }; - } else { - mount_endpoints_and_merged_docs! { - rocket, "/".to_owned(), settings, - "/" => (vec![], custom_openapi_spec()), - "" => openapi_get_routes_spec![root::root], - "/users" => users::routes(), - "/bots" => bots::routes(), - "/channels" => channels::routes(), - "/servers" => servers::routes(), - "/invites" => invites::routes(), - "/custom" => customisation::routes(), - "/safety" => safety::routes(), - "/auth/account" => rocket_authifier::routes::account::routes(), - "/auth/session" => rocket_authifier::routes::session::routes(), - "/auth/mfa" => rocket_authifier::routes::mfa::routes(), - "/onboard" => onboard::routes(), - "/policy" => policy::routes(), - "/push" => push::routes(), - "/sync" => sync::routes() - }; - } - - if config.features.webhooks_enabled { - mount_endpoints_and_merged_docs! { - rocket, "/0.8".to_owned(), settings, - "/" => (vec![], custom_openapi_spec()), - "" => openapi_get_routes_spec![root::root], - "/users" => users::routes(), - "/bots" => bots::routes(), - "/channels" => channels::routes(), - "/servers" => servers::routes(), - "/invites" => invites::routes(), - "/custom" => customisation::routes(), - "/safety" => safety::routes(), - "/auth/account" => rocket_authifier::routes::account::routes(), - "/auth/session" => rocket_authifier::routes::session::routes(), - "/auth/mfa" => rocket_authifier::routes::mfa::routes(), - "/onboard" => onboard::routes(), - "/push" => push::routes(), - "/sync" => sync::routes(), - "/webhooks" => webhooks::routes() - }; - } else { - mount_endpoints_and_merged_docs! { - rocket, "/0.8".to_owned(), settings, - "/" => (vec![], custom_openapi_spec()), - "" => openapi_get_routes_spec![root::root], - "/users" => users::routes(), - "/bots" => bots::routes(), - "/channels" => channels::routes(), - "/servers" => servers::routes(), - "/invites" => invites::routes(), - "/custom" => customisation::routes(), - "/safety" => safety::routes(), - "/auth/account" => rocket_authifier::routes::account::routes(), - "/auth/session" => rocket_authifier::routes::session::routes(), - "/auth/mfa" => rocket_authifier::routes::mfa::routes(), - "/onboard" => onboard::routes(), - "/push" => push::routes(), - "/sync" => sync::routes() - }; - } + rocket = rocket.mount("/webhooks", webhooks::routes()); + }; rocket } -fn custom_openapi_spec() -> OpenApi { - use revolt_rocket_okapi::revolt_okapi::openapi3::*; +#[derive(OpenApi)] +#[openapi( + info( + title = "Stoat API", + description = "Open source user-first chat platform.", + terms_of_service = "https://stoat.chat/terms", + contact( + name = "Stoat Support", + url = "https://stoat.chat", + email = "contact@stoat.chat", + ), + license( + name = "AGPLv3", + url = "https://github.com/stoatchat/stoatchat/blob/master/LICENSE", + ), + ), + servers( + ( + description = "Stoat Production", + url = "https://stoat.chat/api", + ), + ( + description = "Stoat Production", + url = "https://api.stoat.chat" + ) + ), + external_docs( + url = "https://developers.stoat.chat", + description = "Revolt Developer Documentation", + ), + tags( + ( + name = "Core", + description = "Use in your applications to determine information about the Revolt node" + ), + ( + name = "User Information", + description = "Query and fetch users on Revolt" + ), + ( + name = "Direct Messaging", + description = "Direct message other users on Revolt" + ), + ( + name = "Relationships", + description = "Manage your friendships and block list on the platform" + ), + ( + name = "Bots", + description = "Create and edit bots" + ), + ( + name = "Channel Information", + description = "Query and fetch channels on Revolt" + ), + ( + name = "Channel Invites", + description = "Create and manage invites for channels" + ), + ( + name = "Channel Permissions", + description = "Manage permissions for channels" + ), + ( + name = "Messaging", + description = "Send and manipulate messages" + ), + ( + name = "Groups", + description = "Create, invite users and manipulate groups" + ), + ( + name = "Voice", + description = "Join and talk with other users" + ), + ( + name = "Server Information", + description = "Query and fetch servers on Revolt" + ), + ( + name = "Server Members", + description = "Find and edit server members" + ), + ( + name = "Server Permissions", + description = "Manage permissions for servers" + ), + ( + name = "Invites", + description = "View, join and delete invites" + ), + ( + name = "Account", + description = "Manage your account" + ), + ( + name = "Session", + description = "Create and manage sessions" + ), + ( + name = "MFA", + description = "Multi-factor Authentication" + ), + ( + name = "Onboarding", + description = "After signing up to Revolt, users must pick a unique username" + ), + ( + name = "Sync", + description = "Upload and retrieve any JSON data between clients" + ), + ( + name = "Web Push", + description = "Subscribe to and receive Revolt push notifications while offline" + ), + ( + name = "Webhooks", + description = "Send messages from 3rd party services" + ), + ), + paths( + root::root, + ), + nest( + ( + path = "/users", + api = users::ApiDoc, + ), + ( + path = "/bots", + api = bots::ApiDoc, + ), + ( + path = "/channels", + api = channels::ApiDoc, + ), + ( + path = "/servers", + api = servers::ApiDoc, + ), + ( + path = "/invites", + api = invites::ApiDoc, + ), + ( + path = "/custom", + api = customisation::ApiDoc, + ), + ( + path = "/safety", + api = safety::ApiDoc, + ), + ( + path = "/auth/account", + api = rocket_authifier::routes::account::ApiDoc, + ), + ( + path = "/auth/session", + api = rocket_authifier::routes::session::ApiDoc, + ), + ( + path = "/auth/mfa", + api = rocket_authifier::routes::mfa::ApiDoc, + ), + ( + path = "/onboard", + api = onboard::ApiDoc, + ), + ( + path = "/policy", + api = policy::ApiDoc, + ), + ( + path = "/push", + api = push::ApiDoc, + ), + ( + path = "/sync", + api = sync::ApiDoc, + ), + ( + path = "/webhooks", + api = webhooks::ApiDoc, + ), - let mut extensions = schemars::Map::new(); - extensions.insert( - "x-logo".to_owned(), - json!({ - "url": "https://revolt.chat/header.png", - "altText": "Revolt Header" - }), - ); + ), + modifiers( + &Extensions, + &SecurityAddon, + &TokenSecurity, + ), +)] +pub struct ApiDoc; - extensions.insert( - "x-tagGroups".to_owned(), - json!([ - { - "name": "Revolt", - "tags": [ - "Core" - ] - }, - { - "name": "Users", - "tags": [ - "User Information", - "Direct Messaging", - "Relationships" - ] - }, - { - "name": "Bots", - "tags": [ - "Bots" - ] - }, - { - "name": "Channels", - "tags": [ - "Channel Information", - "Channel Invites", - "Channel Permissions", - "Messaging", - "Interactions", - "Groups", - "Voice", - "Webhooks", - ] - }, - { - "name": "Servers", - "tags": [ - "Server Information", - "Server Members", - "Server Permissions" - ] - }, - { - "name": "Invites", - "tags": [ - "Invites" - ] - }, - { - "name": "Customisation", - "tags": [ - "Emojis" - ] - }, - { - "name": "Platform Administration", - "tags": [ - "Admin", - "User Safety" - ] - }, - { - "name": "Authentication", - "tags": [ - "Account", - "Session", - "Onboarding", - "MFA" - ] - }, - { - "name": "Miscellaneous", - "tags": [ - "Sync", - "Web Push" - ] - } - ]), - ); - - OpenApi { - openapi: OpenApi::default_version(), - info: Info { - title: "Revolt API".to_owned(), - description: Some("Open source user-first chat platform.".to_owned()), - terms_of_service: Some("https://revolt.chat/terms".to_owned()), - contact: Some(Contact { - name: Some("Revolt Support".to_owned()), - url: Some("https://revolt.chat".to_owned()), - email: Some("contact@revolt.chat".to_owned()), - ..Default::default() - }), - license: Some(License { - name: "AGPLv3".to_owned(), - url: Some("https://github.com/revoltchat/delta/blob/master/LICENSE".to_owned()), - ..Default::default() - }), - version: env!("CARGO_PKG_VERSION").to_string(), - ..Default::default() - }, - servers: vec![ - Server { - url: "https://api.revolt.chat".to_owned(), - description: Some("Revolt Production".to_owned()), - ..Default::default() - }, - Server { - url: "https://revolt.chat/api".to_owned(), - description: Some("Revolt Staging".to_owned()), - ..Default::default() - }, - Server { - url: "http://local.revolt.chat:14702".to_owned(), - description: Some("Local Revolt Environment".to_owned()), - ..Default::default() - }, - Server { - url: "http://local.revolt.chat:14702/0.8".to_owned(), - description: Some("Local Revolt Environment (v0.8)".to_owned()), - ..Default::default() - }, - ], - external_docs: Some(ExternalDocs { - url: "https://developers.revolt.chat".to_owned(), - description: Some("Revolt Developer Documentation".to_owned()), - ..Default::default() - }), - extensions, - tags: vec![ - Tag { - name: "Core".to_owned(), - description: Some( - "Use in your applications to determine information about the Revolt node" - .to_owned(), - ), - ..Default::default() - }, - Tag { - name: "User Information".to_owned(), - description: Some("Query and fetch users on Revolt".to_owned()), - ..Default::default() - }, - Tag { - name: "Direct Messaging".to_owned(), - description: Some("Direct message other users on Revolt".to_owned()), - ..Default::default() - }, - Tag { - name: "Relationships".to_owned(), - description: Some( - "Manage your friendships and block list on the platform".to_owned(), - ), - ..Default::default() - }, - Tag { - name: "Bots".to_owned(), - description: Some("Create and edit bots".to_owned()), - ..Default::default() - }, - Tag { - name: "Channel Information".to_owned(), - description: Some("Query and fetch channels on Revolt".to_owned()), - ..Default::default() - }, - Tag { - name: "Channel Invites".to_owned(), - description: Some("Create and manage invites for channels".to_owned()), - ..Default::default() - }, - Tag { - name: "Channel Permissions".to_owned(), - description: Some("Manage permissions for channels".to_owned()), - ..Default::default() - }, - Tag { - name: "Messaging".to_owned(), - description: Some("Send and manipulate messages".to_owned()), - ..Default::default() - }, - Tag { - name: "Groups".to_owned(), - description: Some("Create, invite users and manipulate groups".to_owned()), - ..Default::default() - }, - Tag { - name: "Voice".to_owned(), - description: Some("Join and talk with other users".to_owned()), - ..Default::default() - }, - Tag { - name: "Server Information".to_owned(), - description: Some("Query and fetch servers on Revolt".to_owned()), - ..Default::default() - }, - Tag { - name: "Server Members".to_owned(), - description: Some("Find and edit server members".to_owned()), - ..Default::default() - }, - Tag { - name: "Server Permissions".to_owned(), - description: Some("Manage permissions for servers".to_owned()), - ..Default::default() - }, - Tag { - name: "Invites".to_owned(), - description: Some("View, join and delete invites".to_owned()), - ..Default::default() - }, - Tag { - name: "Account".to_owned(), - description: Some("Manage your account".to_owned()), - ..Default::default() - }, - Tag { - name: "Session".to_owned(), - description: Some("Create and manage sessions".to_owned()), - ..Default::default() - }, - Tag { - name: "MFA".to_owned(), - description: Some("Multi-factor Authentication".to_owned()), - ..Default::default() - }, - Tag { - name: "Onboarding".to_owned(), - description: Some( - "After signing up to Revolt, users must pick a unique username".to_owned(), - ), - ..Default::default() - }, - Tag { - name: "Sync".to_owned(), - description: Some("Upload and retrieve any JSON data between clients".to_owned()), - ..Default::default() - }, - Tag { - name: "Web Push".to_owned(), - description: Some( - "Subscribe to and receive Revolt push notifications while offline".to_owned(), - ), - ..Default::default() - }, - Tag { - name: "Webhooks".to_owned(), - description: Some("Send messages from 3rd party services".to_owned()), - ..Default::default() - }, - ], - ..Default::default() +struct Extensions; +impl Modify for Extensions { + fn modify(&self, utoipa: &mut OpenApi) { + utoipa.extensions = Some( + ExtensionsBuilder::new() + .add( + "tagGroups", + json!([ + { + "name": "Revolt", + "tags": [ + "Core" + ] + }, + { + "name": "Users", + "tags": [ + "User Information", + "Direct Messaging", + "Relationships" + ] + }, + { + "name": "Bots", + "tags": [ + "Bots" + ] + }, + { + "name": "Channels", + "tags": [ + "Channel Information", + "Channel Invites", + "Channel Permissions", + "Messaging", + "Interactions", + "Groups", + "Voice", + "Webhooks", + ] + }, + { + "name": "Servers", + "tags": [ + "Server Information", + "Server Members", + "Server Permissions" + ] + }, + { + "name": "Invites", + "tags": [ + "Invites" + ] + }, + { + "name": "Customisation", + "tags": [ + "Emojis" + ] + }, + { + "name": "Platform Administration", + "tags": [ + "Admin", + "User Safety" + ] + }, + { + "name": "Authentication", + "tags": [ + "Account", + "Session", + "Onboarding", + "MFA" + ] + }, + { + "name": "Miscellaneous", + "tags": [ + "Sync", + "Web Push" + ] + } + ]), + ) + .build(), + ); } } diff --git a/crates/delta/src/routes/onboard/complete.rs b/crates/delta/src/routes/onboard/complete.rs index e3621a19..8ce0ed93 100644 --- a/crates/delta/src/routes/onboard/complete.rs +++ b/crates/delta/src/routes/onboard/complete.rs @@ -16,7 +16,7 @@ use validator::Validate; pub static RE_USERNAME: Lazy = Lazy::new(|| Regex::new(r"^(\p{L}|[\d_.-])+$").unwrap()); /// # New User Data -#[derive(Validate, Serialize, Deserialize, JsonSchema)] +#[derive(Validate, Serialize, Deserialize, ToSchema)] pub struct DataOnboard { /// New username which will be used to identify the user on the platform #[validate(length(min = 2, max = 32), regex = "RE_USERNAME")] @@ -26,7 +26,7 @@ pub struct DataOnboard { /// # Complete Onboarding /// /// This sets a new username, completes onboarding and allows a user to start using Revolt. -#[openapi(tag = "Onboarding")] +#[utoipa::path(tag = "Onboarding")] #[post("/complete", data = "")] pub async fn complete( db: &State, diff --git a/crates/delta/src/routes/onboard/hello.rs b/crates/delta/src/routes/onboard/hello.rs index 92a0a8dd..537efc8e 100644 --- a/crates/delta/src/routes/onboard/hello.rs +++ b/crates/delta/src/routes/onboard/hello.rs @@ -5,7 +5,7 @@ use rocket::serde::json::Json; use serde::Serialize; /// # Onboarding Status -#[derive(Serialize, JsonSchema)] +#[derive(Serialize, ToSchema)] pub struct DataHello { /// Whether onboarding is required onboarding: bool, @@ -14,7 +14,7 @@ pub struct DataHello { /// # Check Onboarding Status /// /// This will tell you whether the current account requires onboarding or whether you can continue to send requests as usual. You may skip calling this if you're restoring an existing session. -#[openapi(tag = "Onboarding")] +#[utoipa::path(tag = "Onboarding")] #[get("/hello")] pub async fn hello(_session: Session, user: Option) -> Json { Json(DataHello { diff --git a/crates/delta/src/routes/onboard/mod.rs b/crates/delta/src/routes/onboard/mod.rs index ff07d8cf..2c85421b 100644 --- a/crates/delta/src/routes/onboard/mod.rs +++ b/crates/delta/src/routes/onboard/mod.rs @@ -1,9 +1,17 @@ -use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; use rocket::Route; mod complete; mod hello; -pub fn routes() -> (Vec, OpenApi) { - openapi_get_routes_spec![hello::hello, complete::complete] +#[derive(OpenApi)] +#[openapi( + paths( + hello::hello, complete::complete + ) +)] +pub struct ApiDoc; + + +pub fn routes() -> Vec { + routes![hello::hello, complete::complete] } diff --git a/crates/delta/src/routes/policy/acknowledge_policy_changes.rs b/crates/delta/src/routes/policy/acknowledge_policy_changes.rs index 8b69efda..f9f5aab1 100644 --- a/crates/delta/src/routes/policy/acknowledge_policy_changes.rs +++ b/crates/delta/src/routes/policy/acknowledge_policy_changes.rs @@ -6,7 +6,7 @@ use rocket::State; /// # Acknowledge Policy Changes /// /// Accept/acknowledge changes to platform policy. -#[openapi(tag = "Policy")] +#[utoipa::path(tag = "Policy")] #[post("/acknowledge")] pub async fn acknowledge_policy_changes(db: &State, user: User) -> Result<()> { db.acknowledge_policy_changes(&user.id).await diff --git a/crates/delta/src/routes/policy/mod.rs b/crates/delta/src/routes/policy/mod.rs index 77831b1f..118c92c9 100644 --- a/crates/delta/src/routes/policy/mod.rs +++ b/crates/delta/src/routes/policy/mod.rs @@ -1,10 +1,18 @@ -use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; use rocket::Route; mod acknowledge_policy_changes; -pub fn routes() -> (Vec, OpenApi) { - openapi_get_routes_spec![ +#[derive(OpenApi)] +#[openapi( + paths( + acknowledge_policy_changes::acknowledge_policy_changes + ) +)] +pub struct ApiDoc; + + +pub fn routes() -> Vec { + routes![ // Policy acknowledge_policy_changes::acknowledge_policy_changes, ] diff --git a/crates/delta/src/routes/push/mod.rs b/crates/delta/src/routes/push/mod.rs index 3b55d393..82c0bc9f 100644 --- a/crates/delta/src/routes/push/mod.rs +++ b/crates/delta/src/routes/push/mod.rs @@ -1,9 +1,17 @@ -use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; use rocket::Route; mod subscribe; mod unsubscribe; -pub fn routes() -> (Vec, OpenApi) { - openapi_get_routes_spec![subscribe::subscribe, unsubscribe::unsubscribe] +#[derive(OpenApi)] +#[openapi( + paths( + subscribe::subscribe, + unsubscribe::unsubscribe + ) +)] +pub struct ApiDoc; + +pub fn routes() -> Vec { + routes![subscribe::subscribe, unsubscribe::unsubscribe] } diff --git a/crates/delta/src/routes/push/subscribe.rs b/crates/delta/src/routes/push/subscribe.rs index 0d3d09e4..6e4ba84d 100644 --- a/crates/delta/src/routes/push/subscribe.rs +++ b/crates/delta/src/routes/push/subscribe.rs @@ -11,7 +11,7 @@ use rocket_empty::EmptyResponse; /// Create a new Web Push subscription. /// /// If an existing subscription exists on this session, it will be removed. -#[openapi(tag = "Web Push")] +#[utoipa::path(tag = "Web Push")] #[post("/subscribe", data = "")] pub async fn subscribe( authifier: &State, diff --git a/crates/delta/src/routes/push/unsubscribe.rs b/crates/delta/src/routes/push/unsubscribe.rs index dcc9179f..df5c9ad8 100644 --- a/crates/delta/src/routes/push/unsubscribe.rs +++ b/crates/delta/src/routes/push/unsubscribe.rs @@ -8,7 +8,7 @@ use rocket::State; /// # Unsubscribe /// /// Remove the Web Push subscription associated with the current session. -#[openapi(tag = "Web Push")] +#[utoipa::path(tag = "Web Push")] #[post("/unsubscribe")] pub async fn unsubscribe( authifier: &State, diff --git a/crates/delta/src/routes/root.rs b/crates/delta/src/routes/root.rs index c90c53c0..ac988373 100644 --- a/crates/delta/src/routes/root.rs +++ b/crates/delta/src/routes/root.rs @@ -1,112 +1,34 @@ use revolt_config::config; +use revolt_models::v0; use revolt_result::Result; use rocket::serde::json::Json; -use serde::Serialize; - -/// # hCaptcha Configuration -#[derive(Serialize, JsonSchema, Debug)] -pub struct CaptchaFeature { - /// Whether captcha is enabled - pub enabled: bool, - /// Client key used for solving captcha - pub key: String, -} - -/// # Generic Service Configuration -#[derive(Serialize, JsonSchema, Debug)] -pub struct Feature { - /// Whether the service is enabled - pub enabled: bool, - /// URL pointing to the service - pub url: String, -} - -/// # Voice Server Configuration -#[derive(Serialize, JsonSchema, Debug)] -pub struct VoiceFeature { - /// Whether voice is enabled - pub enabled: bool, - /// URL pointing to the voice API - pub url: String, - /// URL pointing to the voice WebSocket server - pub ws: String, -} - -/// # Feature Configuration -#[derive(Serialize, JsonSchema, Debug)] -pub struct RevoltFeatures { - /// hCaptcha configuration - pub captcha: CaptchaFeature, - /// Whether email verification is enabled - pub email: bool, - /// Whether this server is invite only - pub invite_only: bool, - /// File server service configuration - pub autumn: Feature, - /// Proxy service configuration - pub january: Feature, - /// Voice server configuration - pub voso: VoiceFeature, -} - -/// # Build Information -#[derive(Serialize, JsonSchema, Debug)] -pub struct BuildInformation { - /// Commit Hash - pub commit_sha: String, - /// Commit Timestamp - pub commit_timestamp: String, - /// Git Semver - pub semver: String, - /// Git Origin URL - pub origin_url: String, - /// Build Timestamp - pub timestamp: String, -} - -/// # Server Configuration -#[derive(Serialize, JsonSchema, Debug)] -pub struct RevoltConfig { - /// Revolt API Version - pub revolt: String, - /// Features enabled on this Revolt node - pub features: RevoltFeatures, - /// WebSocket URL - pub ws: String, - /// URL pointing to the client serving this node - pub app: String, - /// Web Push VAPID public key - pub vapid: String, - /// Build information - pub build: BuildInformation, -} /// # Query Node /// /// Fetch the server configuration for this Revolt instance. -#[openapi(tag = "Core")] +#[utoipa::path(tag = "Core")] #[get("/")] -pub async fn root() -> Result> { +pub async fn root() -> Result> { let config = config().await; - Ok(Json(RevoltConfig { + Ok(Json(v0::RevoltConfig { revolt: env!("CARGO_PKG_VERSION").to_string(), - features: RevoltFeatures { - captcha: CaptchaFeature { + features: v0::RevoltFeatures { + captcha: v0::CaptchaFeature { enabled: !config.api.security.captcha.hcaptcha_key.is_empty(), key: config.api.security.captcha.hcaptcha_sitekey, }, email: !config.api.smtp.host.is_empty(), invite_only: config.api.registration.invite_only, - autumn: Feature { + autumn: v0::Feature { enabled: !config.hosts.autumn.is_empty(), url: config.hosts.autumn, }, - january: Feature { + january: v0::Feature { enabled: !config.hosts.january.is_empty(), url: config.hosts.january, }, - voso: VoiceFeature { + voso: v0::VoiceFeature { enabled: !config.hosts.voso_legacy.is_empty(), url: config.hosts.voso_legacy, ws: config.hosts.voso_legacy_ws, @@ -115,7 +37,7 @@ pub async fn root() -> Result> { ws: config.hosts.events, app: config.hosts.app, vapid: config.pushd.vapid.public_key, - build: BuildInformation { + build: v0::BuildInformation { commit_sha: option_env!("VERGEN_GIT_SHA") .unwrap_or_else(|| "") .to_string(), diff --git a/crates/delta/src/routes/safety/mod.rs b/crates/delta/src/routes/safety/mod.rs index df759a43..2416e643 100644 --- a/crates/delta/src/routes/safety/mod.rs +++ b/crates/delta/src/routes/safety/mod.rs @@ -1,10 +1,17 @@ -use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; use rocket::Route; mod report_content; -pub fn routes() -> (Vec, OpenApi) { - openapi_get_routes_spec![ +#[derive(OpenApi)] +#[openapi( + paths( + report_content::report_content + ) +)] +pub struct ApiDoc; + +pub fn routes() -> Vec { + routes![ // Reports report_content::report_content, ] diff --git a/crates/delta/src/routes/safety/report_content.rs b/crates/delta/src/routes/safety/report_content.rs index 1d5c2356..587d9439 100644 --- a/crates/delta/src/routes/safety/report_content.rs +++ b/crates/delta/src/routes/safety/report_content.rs @@ -8,7 +8,7 @@ use validator::Validate; use rocket::{serde::json::Json, State}; /// # Report Data -#[derive(Validate, Deserialize, JsonSchema)] +#[derive(Validate, Deserialize, ToSchema)] pub struct DataReportContent { /// Content being reported content: ReportedContent, @@ -21,7 +21,7 @@ pub struct DataReportContent { /// # Report Content /// /// Report a piece of content to the moderation team. -#[openapi(tag = "User Safety")] +#[utoipa::path(tag = "User Safety")] #[post("/report", data = "")] pub async fn report_content( db: &State, diff --git a/crates/delta/src/routes/servers/ban_create.rs b/crates/delta/src/routes/servers/ban_create.rs index 13c0ef3b..609497e9 100644 --- a/crates/delta/src/routes/servers/ban_create.rs +++ b/crates/delta/src/routes/servers/ban_create.rs @@ -12,7 +12,7 @@ use validator::Validate; /// # Ban User /// /// Ban a user by their id. -#[openapi(tag = "Server Members")] +#[utoipa::path(tag = "Server Members")] #[put("//bans/", data = "")] pub async fn ban( db: &State, diff --git a/crates/delta/src/routes/servers/ban_list.rs b/crates/delta/src/routes/servers/ban_list.rs index 827e7dc3..34726db9 100644 --- a/crates/delta/src/routes/servers/ban_list.rs +++ b/crates/delta/src/routes/servers/ban_list.rs @@ -12,7 +12,7 @@ use rocket::State; /// # Fetch Bans /// /// Fetch all bans on a server. -#[openapi(tag = "Server Members")] +#[utoipa::path(tag = "Server Members")] #[get("//bans")] pub async fn list( db: &State, diff --git a/crates/delta/src/routes/servers/ban_remove.rs b/crates/delta/src/routes/servers/ban_remove.rs index f01bc8b0..b1468a96 100644 --- a/crates/delta/src/routes/servers/ban_remove.rs +++ b/crates/delta/src/routes/servers/ban_remove.rs @@ -10,7 +10,7 @@ use rocket_empty::EmptyResponse; /// # Unban user /// /// Remove a user's ban. -#[openapi(tag = "Server Members")] +#[utoipa::path(tag = "Server Members")] #[delete("//bans/")] pub async fn unban( db: &State, diff --git a/crates/delta/src/routes/servers/channel_create.rs b/crates/delta/src/routes/servers/channel_create.rs index 53aa616d..a633f4fc 100644 --- a/crates/delta/src/routes/servers/channel_create.rs +++ b/crates/delta/src/routes/servers/channel_create.rs @@ -11,7 +11,7 @@ use validator::Validate; /// # Create Channel /// /// Create a new Text or Voice channel. -#[openapi(tag = "Server Information")] +#[utoipa::path(tag = "Server Information")] #[post("//channels", data = "")] pub async fn create_server_channel( db: &State, diff --git a/crates/delta/src/routes/servers/emoji_list.rs b/crates/delta/src/routes/servers/emoji_list.rs index bd19c73e..db19499a 100644 --- a/crates/delta/src/routes/servers/emoji_list.rs +++ b/crates/delta/src/routes/servers/emoji_list.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch Server Emoji /// /// Fetch all emoji on a server. -#[openapi(tag = "Server Customisation")] +#[utoipa::path(tag = "Server Customisation")] #[get("//emojis")] pub async fn list_emoji( db: &State, diff --git a/crates/delta/src/routes/servers/invites_fetch.rs b/crates/delta/src/routes/servers/invites_fetch.rs index b6314207..f43554ad 100644 --- a/crates/delta/src/routes/servers/invites_fetch.rs +++ b/crates/delta/src/routes/servers/invites_fetch.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch Invites /// /// Fetch all server invites. -#[openapi(tag = "Server Members")] +#[utoipa::path(tag = "Server Members")] #[get("//invites")] pub async fn invites( db: &State, diff --git a/crates/delta/src/routes/servers/member_edit.rs b/crates/delta/src/routes/servers/member_edit.rs index 74ac5d72..08f58c08 100644 --- a/crates/delta/src/routes/servers/member_edit.rs +++ b/crates/delta/src/routes/servers/member_edit.rs @@ -14,7 +14,7 @@ use validator::Validate; /// # Edit Member /// /// Edit a member by their id. -#[openapi(tag = "Server Members")] +#[utoipa::path(tag = "Server Members")] #[patch("//members/", data = "")] pub async fn edit( db: &State, diff --git a/crates/delta/src/routes/servers/member_experimental_query.rs b/crates/delta/src/routes/servers/member_experimental_query.rs index 8126b9f2..27809dfd 100644 --- a/crates/delta/src/routes/servers/member_experimental_query.rs +++ b/crates/delta/src/routes/servers/member_experimental_query.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; use serde::{Deserialize, Serialize}; /// # Query Parameters -#[derive(Deserialize, JsonSchema, FromForm)] +#[derive(Deserialize, ToSchema, FromForm)] pub struct OptionsQueryMembers { /// String to search for query: String, @@ -20,7 +20,7 @@ pub struct OptionsQueryMembers { } /// # Query members by name -#[derive(Serialize, JsonSchema)] +#[derive(Serialize, ToSchema)] pub struct MemberQueryResponse { /// List of members members: Vec, @@ -31,7 +31,7 @@ pub struct MemberQueryResponse { /// # Query members by name /// /// Query members by a given name, this API is not stable and will be removed in the future. -#[openapi(tag = "Server Members")] +#[utoipa::path(tag = "Server Members")] #[get("//members_experimental_query?")] pub async fn member_experimental_query( db: &State, diff --git a/crates/delta/src/routes/servers/member_fetch.rs b/crates/delta/src/routes/servers/member_fetch.rs index c05935be..5ec1fc3f 100644 --- a/crates/delta/src/routes/servers/member_fetch.rs +++ b/crates/delta/src/routes/servers/member_fetch.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch Member /// /// Retrieve a member. -#[openapi(tag = "Server Members")] +#[utoipa::path(tag = "Server Members")] #[get("//members/?")] pub async fn fetch( db: &State, diff --git a/crates/delta/src/routes/servers/member_fetch_all.rs b/crates/delta/src/routes/servers/member_fetch_all.rs index cc85b968..b7eb30d7 100644 --- a/crates/delta/src/routes/servers/member_fetch_all.rs +++ b/crates/delta/src/routes/servers/member_fetch_all.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch Members /// /// Fetch all server members. -#[openapi(tag = "Server Members")] +#[utoipa::path(tag = "Server Members")] #[get("//members?")] pub async fn fetch_all( db: &State, diff --git a/crates/delta/src/routes/servers/member_remove.rs b/crates/delta/src/routes/servers/member_remove.rs index 23004267..b96e3498 100644 --- a/crates/delta/src/routes/servers/member_remove.rs +++ b/crates/delta/src/routes/servers/member_remove.rs @@ -10,7 +10,7 @@ use rocket_empty::EmptyResponse; /// # Kick Member /// /// Removes a member from the server. -#[openapi(tag = "Server Members")] +#[utoipa::path(tag = "Server Members")] #[delete("//members/")] pub async fn kick( db: &State, diff --git a/crates/delta/src/routes/servers/mod.rs b/crates/delta/src/routes/servers/mod.rs index 78b96dbd..b9bd8f4a 100644 --- a/crates/delta/src/routes/servers/mod.rs +++ b/crates/delta/src/routes/servers/mod.rs @@ -1,4 +1,3 @@ -use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; use rocket::Route; mod ban_create; @@ -25,8 +24,38 @@ mod server_delete; mod server_edit; mod server_fetch; -pub fn routes() -> (Vec, OpenApi) { - openapi_get_routes_spec![ +#[derive(OpenApi)] +#[openapi( + paths( + server_create::create_server, + server_delete::delete, + server_fetch::fetch, + server_edit::edit, + server_ack::ack, + channel_create::create_server_channel, + member_fetch_all::fetch_all, + member_remove::kick, + member_fetch::fetch, + member_edit::edit, + member_experimental_query::member_experimental_query, + ban_create::ban, + ban_remove::unban, + ban_list::list, + invites_fetch::invites, + roles_create::create, + roles_edit::edit, + roles_fetch::fetch, + roles_delete::delete, + permissions_set::set_role_permission, + permissions_set_default::set_default_server_permissions, + emoji_list::list_emoji, + roles_edit_positions::edit_role_ranks + ) +)] +pub struct ApiDoc; + +pub fn routes() -> Vec { + routes![ server_create::create_server, server_delete::delete, server_fetch::fetch, diff --git a/crates/delta/src/routes/servers/permissions_set.rs b/crates/delta/src/routes/servers/permissions_set.rs index f0d8099f..2f57b2bf 100644 --- a/crates/delta/src/routes/servers/permissions_set.rs +++ b/crates/delta/src/routes/servers/permissions_set.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; /// # Set Role Permission /// /// Sets permissions for the specified role in the server. -#[openapi(tag = "Server Permissions")] +#[utoipa::path(tag = "Server Permissions")] #[put("//permissions/", data = "", rank = 2)] pub async fn set_role_permission( db: &State, diff --git a/crates/delta/src/routes/servers/permissions_set_default.rs b/crates/delta/src/routes/servers/permissions_set_default.rs index 6ee5b813..c965ca33 100644 --- a/crates/delta/src/routes/servers/permissions_set_default.rs +++ b/crates/delta/src/routes/servers/permissions_set_default.rs @@ -12,7 +12,7 @@ use rocket::{serde::json::Json, State}; /// # Set Default Permission /// /// Sets permissions for the default role in this server. -#[openapi(tag = "Server Permissions")] +#[utoipa::path(tag = "Server Permissions")] #[put("//permissions/default", data = "", rank = 1)] pub async fn set_default_server_permissions( db: &State, diff --git a/crates/delta/src/routes/servers/roles_create.rs b/crates/delta/src/routes/servers/roles_create.rs index e68d5ea6..eabe90f2 100644 --- a/crates/delta/src/routes/servers/roles_create.rs +++ b/crates/delta/src/routes/servers/roles_create.rs @@ -12,7 +12,7 @@ use validator::Validate; /// # Create Role /// /// Creates a new server role. -#[openapi(tag = "Server Permissions")] +#[utoipa::path(tag = "Server Permissions")] #[post("//roles", data = "")] pub async fn create( db: &State, diff --git a/crates/delta/src/routes/servers/roles_delete.rs b/crates/delta/src/routes/servers/roles_delete.rs index 6867b647..f0f80a4f 100644 --- a/crates/delta/src/routes/servers/roles_delete.rs +++ b/crates/delta/src/routes/servers/roles_delete.rs @@ -10,7 +10,7 @@ use rocket_empty::EmptyResponse; /// # Delete Role /// /// Delete a server role by its id. -#[openapi(tag = "Server Permissions")] +#[utoipa::path(tag = "Server Permissions")] #[delete("//roles/")] pub async fn delete( db: &State, diff --git a/crates/delta/src/routes/servers/roles_edit.rs b/crates/delta/src/routes/servers/roles_edit.rs index f18bd7d3..4c3041fe 100644 --- a/crates/delta/src/routes/servers/roles_edit.rs +++ b/crates/delta/src/routes/servers/roles_edit.rs @@ -11,7 +11,7 @@ use validator::Validate; /// # Edit Role /// /// Edit a role by its id. -#[openapi(tag = "Server Permissions")] +#[utoipa::path(tag = "Server Permissions")] #[patch("//roles/", data = "", rank = 1)] pub async fn edit( db: &State, diff --git a/crates/delta/src/routes/servers/roles_edit_positions.rs b/crates/delta/src/routes/servers/roles_edit_positions.rs index d27259e5..d3489173 100644 --- a/crates/delta/src/routes/servers/roles_edit_positions.rs +++ b/crates/delta/src/routes/servers/roles_edit_positions.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; /// # Edits server roles ranks /// /// Edit's server role's ranks. -#[openapi(tag = "Server Permissions")] +#[utoipa::path(tag = "Server Permissions")] #[patch("//roles/ranks", data = "")] pub async fn edit_role_ranks( db: &State, diff --git a/crates/delta/src/routes/servers/roles_fetch.rs b/crates/delta/src/routes/servers/roles_fetch.rs index d26365a9..27734e9e 100644 --- a/crates/delta/src/routes/servers/roles_fetch.rs +++ b/crates/delta/src/routes/servers/roles_fetch.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch Role /// /// Fetch a role by its id. -#[openapi(tag = "Server Permissions")] +#[utoipa::path(tag = "Server Permissions")] #[get("//roles/")] pub async fn fetch( db: &State, diff --git a/crates/delta/src/routes/servers/server_ack.rs b/crates/delta/src/routes/servers/server_ack.rs index 789617e1..9162dd40 100644 --- a/crates/delta/src/routes/servers/server_ack.rs +++ b/crates/delta/src/routes/servers/server_ack.rs @@ -10,7 +10,7 @@ use rocket_empty::EmptyResponse; /// # Mark Server As Read /// /// Mark all channels in a server as read. -#[openapi(tag = "Server Information")] +#[utoipa::path(tag = "Server Information")] #[put("//ack")] pub async fn ack(db: &State, user: User, target: Reference<'_>) -> Result { if user.bot.is_some() { diff --git a/crates/delta/src/routes/servers/server_create.rs b/crates/delta/src/routes/servers/server_create.rs index 82f0a8aa..dd1068ba 100644 --- a/crates/delta/src/routes/servers/server_create.rs +++ b/crates/delta/src/routes/servers/server_create.rs @@ -9,7 +9,7 @@ use validator::Validate; /// # Create Server /// /// Create a new server. -#[openapi(tag = "Server Information")] +#[utoipa::path(tag = "Server Information")] #[post("/create", data = "")] pub async fn create_server( db: &State, diff --git a/crates/delta/src/routes/servers/server_delete.rs b/crates/delta/src/routes/servers/server_delete.rs index 1302db5b..49006aac 100644 --- a/crates/delta/src/routes/servers/server_delete.rs +++ b/crates/delta/src/routes/servers/server_delete.rs @@ -8,7 +8,7 @@ use rocket_empty::EmptyResponse; /// # Delete / Leave Server /// /// Deletes a server if owner otherwise leaves. -#[openapi(tag = "Server Information")] +#[utoipa::path(tag = "Server Information")] #[delete("/?")] pub async fn delete( db: &State, diff --git a/crates/delta/src/routes/servers/server_edit.rs b/crates/delta/src/routes/servers/server_edit.rs index 23d79c34..54a97c3a 100644 --- a/crates/delta/src/routes/servers/server_edit.rs +++ b/crates/delta/src/routes/servers/server_edit.rs @@ -13,7 +13,7 @@ use validator::Validate; /// # Edit Server /// /// Edit a server by its id. -#[openapi(tag = "Server Information")] +#[utoipa::path(tag = "Server Information")] #[patch("/", data = "")] pub async fn edit( db: &State, diff --git a/crates/delta/src/routes/servers/server_fetch.rs b/crates/delta/src/routes/servers/server_fetch.rs index 82eb0c7c..8defad7f 100644 --- a/crates/delta/src/routes/servers/server_fetch.rs +++ b/crates/delta/src/routes/servers/server_fetch.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch Server /// /// Fetch a server by its id. -#[openapi(tag = "Server Information")] +#[utoipa::path(tag = "Server Information")] #[get("/?")] pub async fn fetch( db: &State, diff --git a/crates/delta/src/routes/sync/get_settings.rs b/crates/delta/src/routes/sync/get_settings.rs index 12302925..8173ad92 100644 --- a/crates/delta/src/routes/sync/get_settings.rs +++ b/crates/delta/src/routes/sync/get_settings.rs @@ -9,7 +9,7 @@ use rocket::State; /// Fetch settings from server filtered by keys. /// /// This will return an object with the requested keys, each value is a tuple of `(timestamp, value)`, the value is the previously uploaded data. -#[openapi(tag = "Sync")] +#[utoipa::path(tag = "Sync")] #[post("/settings/fetch", data = "")] pub async fn fetch( db: &State, diff --git a/crates/delta/src/routes/sync/get_unreads.rs b/crates/delta/src/routes/sync/get_unreads.rs index 90efcf68..d7f115b8 100644 --- a/crates/delta/src/routes/sync/get_unreads.rs +++ b/crates/delta/src/routes/sync/get_unreads.rs @@ -7,7 +7,7 @@ use rocket::State; /// # Fetch Unreads /// /// Fetch information about unread state on channels. -#[openapi(tag = "Sync")] +#[utoipa::path(tag = "Sync")] #[get("/unreads")] pub async fn unreads(db: &State, user: User) -> Result>> { db.fetch_unreads(&user.id) diff --git a/crates/delta/src/routes/sync/mod.rs b/crates/delta/src/routes/sync/mod.rs index b97c2ad8..a99c58f5 100644 --- a/crates/delta/src/routes/sync/mod.rs +++ b/crates/delta/src/routes/sync/mod.rs @@ -1,10 +1,19 @@ -use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; use rocket::Route; mod get_settings; mod get_unreads; mod set_settings; -pub fn routes() -> (Vec, OpenApi) { - openapi_get_routes_spec![get_settings::fetch, set_settings::set, get_unreads::unreads] +#[derive(OpenApi)] +#[openapi( + paths( + get_settings::fetch, + set_settings::set, + get_unreads::unreads + ) +)] +pub struct ApiDoc; + +pub fn routes() -> Vec { + routes![get_settings::fetch, set_settings::set, get_unreads::unreads] } diff --git a/crates/delta/src/routes/sync/set_settings.rs b/crates/delta/src/routes/sync/set_settings.rs index 361d5871..c3d4edb2 100644 --- a/crates/delta/src/routes/sync/set_settings.rs +++ b/crates/delta/src/routes/sync/set_settings.rs @@ -12,7 +12,7 @@ type Data = HashMap; /// # Set Settings /// /// Upload data to save to settings. -#[openapi(tag = "Sync")] +#[utoipa::path(tag = "Sync")] #[post("/settings/set?", data = "")] pub async fn set( db: &State, diff --git a/crates/delta/src/routes/users/add_friend.rs b/crates/delta/src/routes/users/add_friend.rs index 946c3296..1fafc46d 100644 --- a/crates/delta/src/routes/users/add_friend.rs +++ b/crates/delta/src/routes/users/add_friend.rs @@ -8,7 +8,7 @@ use rocket::State; /// # Accept Friend Request /// /// Accept another user's friend request. -#[openapi(tag = "Relationships")] +#[utoipa::path(tag = "Relationships")] #[put("//friend")] pub async fn add( db: &State, diff --git a/crates/delta/src/routes/users/block_user.rs b/crates/delta/src/routes/users/block_user.rs index cfce993c..19f7139f 100644 --- a/crates/delta/src/routes/users/block_user.rs +++ b/crates/delta/src/routes/users/block_user.rs @@ -8,7 +8,7 @@ use rocket::State; /// # Block User /// /// Block another user by their id. -#[openapi(tag = "Relationships")] +#[utoipa::path(tag = "Relationships")] #[put("//block")] pub async fn block( db: &State, diff --git a/crates/delta/src/routes/users/change_username.rs b/crates/delta/src/routes/users/change_username.rs index 7c3f98c9..2ba7413a 100644 --- a/crates/delta/src/routes/users/change_username.rs +++ b/crates/delta/src/routes/users/change_username.rs @@ -15,7 +15,7 @@ use validator::Validate; pub static RE_USERNAME: Lazy = Lazy::new(|| Regex::new(r"^(\p{L}|[\d_.-])+$").unwrap()); /// # Username Information -#[derive(Validate, Serialize, Deserialize, JsonSchema)] +#[derive(Validate, Serialize, Deserialize, ToSchema)] pub struct DataChangeUsername { /// New username #[validate(length(min = 2, max = 32), regex = "RE_USERNAME")] @@ -28,7 +28,7 @@ pub struct DataChangeUsername { /// # Change Username /// /// Change your username. -#[openapi(tag = "User Information")] +#[utoipa::path(tag = "User Information")] #[patch("/@me/username", data = "")] pub async fn change_username( db: &State, diff --git a/crates/delta/src/routes/users/edit_user.rs b/crates/delta/src/routes/users/edit_user.rs index 535fc05c..fba1f7e7 100644 --- a/crates/delta/src/routes/users/edit_user.rs +++ b/crates/delta/src/routes/users/edit_user.rs @@ -9,7 +9,7 @@ use validator::Validate; /// # Edit User /// /// Edit currently authenticated user. -#[openapi(tag = "User Information")] +#[utoipa::path(tag = "User Information")] #[patch("/", data = "")] pub async fn edit( db: &State, diff --git a/crates/delta/src/routes/users/fetch_dms.rs b/crates/delta/src/routes/users/fetch_dms.rs index d0b39e63..b0ef8856 100644 --- a/crates/delta/src/routes/users/fetch_dms.rs +++ b/crates/delta/src/routes/users/fetch_dms.rs @@ -6,7 +6,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch Direct Message Channels /// /// This fetches your direct messages, including any DM and group DM conversations. -#[openapi(tag = "Direct Messaging")] +#[utoipa::path(tag = "Direct Messaging")] #[get("/dms")] pub async fn direct_messages(db: &State, user: User) -> Result>> { db.find_direct_messages(&user.id) diff --git a/crates/delta/src/routes/users/fetch_profile.rs b/crates/delta/src/routes/users/fetch_profile.rs index e0718d4b..44e360d8 100644 --- a/crates/delta/src/routes/users/fetch_profile.rs +++ b/crates/delta/src/routes/users/fetch_profile.rs @@ -12,7 +12,7 @@ use rocket::{serde::json::Json, State}; /// Retrieve a user's profile data. /// /// Will fail if you do not have permission to access the other user's profile. -#[openapi(tag = "User Information")] +#[utoipa::path(tag = "User Information")] #[get("//profile")] pub async fn profile( db: &State, diff --git a/crates/delta/src/routes/users/fetch_self.rs b/crates/delta/src/routes/users/fetch_self.rs index 02257be9..ee5fc63f 100755 --- a/crates/delta/src/routes/users/fetch_self.rs +++ b/crates/delta/src/routes/users/fetch_self.rs @@ -6,7 +6,7 @@ use rocket::serde::json::Json; /// # Fetch Self /// /// Retrieve your user information. -#[openapi(tag = "User Information")] +#[utoipa::path(tag = "User Information")] #[get("/@me")] pub async fn fetch(user: User) -> Result> { Ok(Json(user.into_self(false).await)) diff --git a/crates/delta/src/routes/users/fetch_user.rs b/crates/delta/src/routes/users/fetch_user.rs index e6aba68e..3c0f6336 100644 --- a/crates/delta/src/routes/users/fetch_user.rs +++ b/crates/delta/src/routes/users/fetch_user.rs @@ -11,9 +11,13 @@ use rocket::{serde::json::Json, State}; /// # Fetch User /// /// Retrieve a user's information. -#[openapi(tag = "User Information")] +#[utoipa::path(tag = "User Information")] #[get("/")] -pub async fn fetch(db: &State, user: User, target: Reference<'_>) -> Result> { +pub async fn fetch( + db: &State, + user: User, + target: Reference<'_>, +) -> Result> { if user.id == target.id { return Ok(Json(user.into_self(false).await)); } diff --git a/crates/delta/src/routes/users/fetch_user_flags.rs b/crates/delta/src/routes/users/fetch_user_flags.rs index df021abb..52498f81 100644 --- a/crates/delta/src/routes/users/fetch_user_flags.rs +++ b/crates/delta/src/routes/users/fetch_user_flags.rs @@ -6,7 +6,7 @@ use rocket::{serde::json::Json, State}; /// # Fetch User Flags /// /// Retrieve a user's flags. -#[openapi(tag = "User Information")] +#[utoipa::path(tag = "User Information")] #[get("//flags")] pub async fn fetch_user_flags( db: &State, diff --git a/crates/delta/src/routes/users/find_mutual.rs b/crates/delta/src/routes/users/find_mutual.rs index 78d5256f..39859270 100644 --- a/crates/delta/src/routes/users/find_mutual.rs +++ b/crates/delta/src/routes/users/find_mutual.rs @@ -11,7 +11,7 @@ use rocket::State; /// # Fetch Mutual Friends, Servers, Groups and DMs /// /// Retrieve a list of mutual friends, servers, groups and DMs with another user. -#[openapi(tag = "Relationships")] +#[utoipa::path(tag = "Relationships")] #[get("//mutual")] pub async fn mutual( db: &State, diff --git a/crates/delta/src/routes/users/get_default_avatar.rs b/crates/delta/src/routes/users/get_default_avatar.rs index 193d291c..a85396e0 100644 --- a/crates/delta/src/routes/users/get_default_avatar.rs +++ b/crates/delta/src/routes/users/get_default_avatar.rs @@ -1,10 +1,9 @@ -use revolt_rocket_okapi::revolt_okapi::openapi3::{self, MediaType, RefOr}; use rocket::http::ContentType; use rocket::response::{self, Responder}; use rocket::{Request, Response}; -use schemars::schema::{InstanceType, SchemaObject, SingleOrVec}; -pub struct CachedFile((ContentType, Vec)); +#[derive(ToSchema)] +pub struct CachedFile(Vec); pub static CACHE_CONTROL: &str = "public, max-age=31536000, immutable"; @@ -16,50 +15,15 @@ impl<'r> Responder<'r, 'static> for CachedFile { } } -impl revolt_rocket_okapi::response::OpenApiResponderInner for CachedFile { - fn responses( - _gen: &mut revolt_rocket_okapi::gen::OpenApiGenerator, - ) -> std::result::Result { - let mut responses = schemars::Map::new(); - let mut content = schemars::Map::new(); - - content.insert( - "image/png".to_owned(), - MediaType { - schema: Some(SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), - format: Some("binary".to_owned()), - ..Default::default() - }), - ..Default::default() - }, - ); - - responses.insert( - "200".to_string(), - RefOr::Object(openapi3::Response { - description: "Default Avatar Picture".to_string(), - content, - ..Default::default() - }), - ); - - Ok(openapi3::Responses { - responses, - ..Default::default() - }) - } -} - /// # Fetch Default Avatar /// /// This returns a default avatar based on the given id. -#[openapi(tag = "User Information")] +#[utoipa::path(tag = "User Information")] #[get("//default_avatar")] -pub async fn default_avatar(target: String) -> CachedFile { - CachedFile(( +pub async fn default_avatar(target: String) -> (ContentType, CachedFile) { + ( ContentType::PNG, - match target.chars().last().unwrap() { + CachedFile(match target.chars().last().unwrap() { '0' | '1' | '2' | '3' | 'S' | 'Z' => include_bytes!("avatars/2.png").to_vec(), '4' | '5' | '6' | '7' | 'T' => include_bytes!("avatars/3.png").to_vec(), '8' | '9' | 'A' | 'B' => include_bytes!("avatars/4.png").to_vec(), @@ -67,6 +31,6 @@ pub async fn default_avatar(target: String) -> CachedFile { 'G' | 'H' | 'J' | 'K' | 'W' => include_bytes!("avatars/6.png").to_vec(), 'M' | 'N' | 'P' | 'Q' | 'X' => include_bytes!("avatars/7.png").to_vec(), _ => include_bytes!("avatars/1.png").to_vec(), - }, - )) + }), + ) } diff --git a/crates/delta/src/routes/users/mod.rs b/crates/delta/src/routes/users/mod.rs index ba7445ac..2dedfa6a 100644 --- a/crates/delta/src/routes/users/mod.rs +++ b/crates/delta/src/routes/users/mod.rs @@ -1,4 +1,3 @@ -use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; use rocket::Route; mod add_friend; @@ -17,8 +16,30 @@ mod remove_friend; mod send_friend_request; mod unblock_user; -pub fn routes() -> (Vec, OpenApi) { - openapi_get_routes_spec![ +#[derive(OpenApi)] +#[openapi( + paths( + fetch_self::fetch, + fetch_user::fetch, + fetch_user_flags::fetch_user_flags, + edit_user::edit, + change_username::change_username, + get_default_avatar::default_avatar, + fetch_profile::profile, + fetch_dms::direct_messages, + open_dm::open_dm, + find_mutual::mutual, + add_friend::add, + remove_friend::remove, + block_user::block, + unblock_user::unblock, + send_friend_request::send_friend_request, + ) +)] +pub struct ApiDoc; + +pub fn routes() -> Vec { + routes![ // User Information fetch_self::fetch, fetch_user::fetch, diff --git a/crates/delta/src/routes/users/open_dm.rs b/crates/delta/src/routes/users/open_dm.rs index 78486523..07c19582 100644 --- a/crates/delta/src/routes/users/open_dm.rs +++ b/crates/delta/src/routes/users/open_dm.rs @@ -12,7 +12,7 @@ use rocket::{serde::json::Json, State}; /// Open a DM with another user. /// /// If the target is oneself, a saved messages channel is returned. -#[openapi(tag = "Direct Messaging")] +#[utoipa::path(tag = "Direct Messaging")] #[get("//dm")] pub async fn open_dm( db: &State, diff --git a/crates/delta/src/routes/users/remove_friend.rs b/crates/delta/src/routes/users/remove_friend.rs index 59b85ecb..1c1fc40f 100644 --- a/crates/delta/src/routes/users/remove_friend.rs +++ b/crates/delta/src/routes/users/remove_friend.rs @@ -8,7 +8,7 @@ use rocket::State; /// # Deny Friend Request / Remove Friend /// /// Denies another user's friend request or removes an existing friend. -#[openapi(tag = "Relationships")] +#[utoipa::path(tag = "Relationships")] #[delete("//friend")] pub async fn remove( db: &State, diff --git a/crates/delta/src/routes/users/send_friend_request.rs b/crates/delta/src/routes/users/send_friend_request.rs index a87334af..ce91be1b 100644 --- a/crates/delta/src/routes/users/send_friend_request.rs +++ b/crates/delta/src/routes/users/send_friend_request.rs @@ -8,7 +8,7 @@ use rocket::State; /// # Send Friend Request /// /// Send a friend request to another user. -#[openapi(tag = "Relationships")] +#[utoipa::path(tag = "Relationships")] #[post("/friend", data = "")] pub async fn send_friend_request( db: &State, diff --git a/crates/delta/src/routes/users/unblock_user.rs b/crates/delta/src/routes/users/unblock_user.rs index a464ecc5..789e7542 100644 --- a/crates/delta/src/routes/users/unblock_user.rs +++ b/crates/delta/src/routes/users/unblock_user.rs @@ -8,7 +8,7 @@ use rocket::State; /// # Unblock User /// /// Unblock another user by their id. -#[openapi(tag = "Relationships")] +#[utoipa::path(tag = "Relationships")] #[delete("//block")] pub async fn unblock( db: &State, diff --git a/crates/delta/src/routes/webhooks/mod.rs b/crates/delta/src/routes/webhooks/mod.rs index 6b0ef448..5760364c 100644 --- a/crates/delta/src/routes/webhooks/mod.rs +++ b/crates/delta/src/routes/webhooks/mod.rs @@ -1,5 +1,4 @@ use rocket::Route; -use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi; mod webhook_delete; mod webhook_delete_token; @@ -10,8 +9,23 @@ mod webhook_fetch_token; mod webhook_fetch; mod webhook_execute_github; -pub fn routes() -> (Vec, OpenApi) { - openapi_get_routes_spec![ +#[derive(OpenApi)] +#[openapi( + paths( + webhook_delete_token::webhook_delete_token, + webhook_delete::webhook_delete, + webhook_edit_token::webhook_edit_token, + webhook_edit::webhook_edit, + webhook_execute_github::webhook_execute_github, + webhook_execute::webhook_execute, + webhook_fetch_token::webhook_fetch_token, + webhook_fetch::webhook_fetch, + ) +)] +pub struct ApiDoc; + +pub fn routes() -> Vec { + routes![ webhook_delete_token::webhook_delete_token, webhook_delete::webhook_delete, webhook_edit_token::webhook_edit_token, diff --git a/crates/delta/src/routes/webhooks/webhook_delete.rs b/crates/delta/src/routes/webhooks/webhook_delete.rs index fcd428d3..3c76bafd 100644 --- a/crates/delta/src/routes/webhooks/webhook_delete.rs +++ b/crates/delta/src/routes/webhooks/webhook_delete.rs @@ -10,7 +10,7 @@ use rocket_empty::EmptyResponse; /// # Deletes a webhook /// /// Deletes a webhook -#[openapi(tag = "Webhooks")] +#[utoipa::path(tag = "Webhooks")] #[delete("/")] pub async fn webhook_delete( db: &State, diff --git a/crates/delta/src/routes/webhooks/webhook_delete_token.rs b/crates/delta/src/routes/webhooks/webhook_delete_token.rs index aa5fc2e9..9d8d9541 100644 --- a/crates/delta/src/routes/webhooks/webhook_delete_token.rs +++ b/crates/delta/src/routes/webhooks/webhook_delete_token.rs @@ -6,7 +6,7 @@ use rocket_empty::EmptyResponse; /// # Deletes a webhook /// /// Deletes a webhook with a token -#[openapi(tag = "Webhooks")] +#[utoipa::path(tag = "Webhooks")] #[delete("//")] pub async fn webhook_delete_token( db: &State, diff --git a/crates/delta/src/routes/webhooks/webhook_edit.rs b/crates/delta/src/routes/webhooks/webhook_edit.rs index aa06930a..c25df675 100644 --- a/crates/delta/src/routes/webhooks/webhook_edit.rs +++ b/crates/delta/src/routes/webhooks/webhook_edit.rs @@ -11,7 +11,7 @@ use validator::Validate; /// # Edits a webhook /// /// Edits a webhook -#[openapi(tag = "Webhooks")] +#[utoipa::path(tag = "Webhooks")] #[patch("/", data = "")] pub async fn webhook_edit( db: &State, diff --git a/crates/delta/src/routes/webhooks/webhook_edit_token.rs b/crates/delta/src/routes/webhooks/webhook_edit_token.rs index 4f4dde0d..212b20ba 100644 --- a/crates/delta/src/routes/webhooks/webhook_edit_token.rs +++ b/crates/delta/src/routes/webhooks/webhook_edit_token.rs @@ -8,7 +8,7 @@ use rocket::{serde::json::Json, State}; /// # Edits a webhook /// /// Edits a webhook with a token -#[openapi(tag = "Webhooks")] +#[utoipa::path(tag = "Webhooks")] #[patch("//", data = "")] pub async fn webhook_edit_token( db: &State, diff --git a/crates/delta/src/routes/webhooks/webhook_execute.rs b/crates/delta/src/routes/webhooks/webhook_execute.rs index f46004f7..56295a70 100644 --- a/crates/delta/src/routes/webhooks/webhook_execute.rs +++ b/crates/delta/src/routes/webhooks/webhook_execute.rs @@ -13,7 +13,7 @@ use validator::Validate; /// # Executes a webhook /// /// Executes a webhook and sends a message -#[openapi(tag = "Webhooks")] +#[utoipa::path(tag = "Webhooks")] #[post("//", data = "")] pub async fn webhook_execute( db: &State, diff --git a/crates/delta/src/routes/webhooks/webhook_execute_github.rs b/crates/delta/src/routes/webhooks/webhook_execute_github.rs index f84ecc63..187a5b7f 100644 --- a/crates/delta/src/routes/webhooks/webhook_execute_github.rs +++ b/crates/delta/src/routes/webhooks/webhook_execute_github.rs @@ -1,19 +1,13 @@ use revolt_database::{util::reference::Reference, Database, Message, AMQP}; use revolt_models::v0::{MessageAuthor, SendableEmbed, Webhook}; use revolt_result::{create_error, Error, Result}; -use revolt_rocket_okapi::{ - gen::OpenApiGenerator, - request::{OpenApiFromRequest, RequestHeaderInput}, - revolt_okapi::openapi3::{MediaType, Parameter, ParameterValue}, -}; use rocket::{http::Status, request::FromRequest, Request, State}; -use schemars::schema::SchemaObject; use serde::{Deserialize, Serialize}; use serde_json::Value; use ulid::Ulid; use validator::Validate; -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubUser { name: Option, email: Option, @@ -35,19 +29,19 @@ pub struct GithubUser { received_events_url: String, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubRepositorySecurityAndAnalysisStatus { status: String, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubRepositorySecurityAndAnalysis { advanced_security: GithubRepositorySecurityAndAnalysisStatus, secret_scanning: GithubRepositorySecurityAndAnalysisStatus, secret_scanning_push_protection: GithubRepositorySecurityAndAnalysisStatus, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubRepositoryLicense { key: Option, name: Option, @@ -56,7 +50,7 @@ pub struct GithubRepositoryLicense { node_id: Option, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubRepositoryCodeOfConduct { key: String, name: String, @@ -65,7 +59,7 @@ pub struct GithubRepositoryCodeOfConduct { html_url: Option, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubRepositoryPermissions { admin: Option, maintain: Option, @@ -74,7 +68,7 @@ pub struct GithubRepositoryPermissions { pull: Option, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubRepository { id: u32, node_id: String, @@ -165,7 +159,7 @@ pub struct GithubRepository { security_and_analysis: Option, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct CommitAuthor { date: Option, email: Option, @@ -173,7 +167,7 @@ pub struct CommitAuthor { username: Option, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubCommit { added: Option>, author: CommitAuthor, @@ -187,7 +181,7 @@ pub struct GithubCommit { tree_id: String, url: String, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubReactions { #[serde(rename = "+1")] plus_one: u32, @@ -203,7 +197,7 @@ pub struct GithubReactions { url: String, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubComment { author_association: String, body: String, @@ -221,7 +215,7 @@ pub struct GithubComment { user: GithubUser, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubDiscussionComment { #[serde(flatten)] comment: GithubComment, @@ -230,7 +224,7 @@ pub struct GithubDiscussionComment { repository_url: String, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubDiscussionCategory { id: u32, node_id: String, @@ -244,7 +238,7 @@ pub struct GithubDiscussionCategory { is_answerable: bool, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubDiscussion { repository_url: String, category: GithubDiscussionCategory, @@ -269,7 +263,7 @@ pub struct GithubDiscussion { timeline_url: String, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] #[serde(rename_all = "snake_case", tag = "action")] #[allow(clippy::large_enum_variant)] pub enum GithubDiscussionEvent { @@ -282,7 +276,7 @@ pub enum GithubDiscussionEvent { }, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] #[serde(rename_all = "snake_case", tag = "action")] #[allow(clippy::large_enum_variant)] pub enum DiscussionCommentEvent { @@ -294,7 +288,7 @@ pub enum DiscussionCommentEvent { Edited {}, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubMilestone { url: String, html_url: String, @@ -314,7 +308,7 @@ pub struct GithubMilestone { due_on: Option, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubLinkedPullRequest { merged_at: Option, diff_url: Option, @@ -322,7 +316,7 @@ pub struct GithubLinkedPullRequest { patch_url: Option, url: Option, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubAppPermissions { issues: Option, checks: Option, @@ -331,7 +325,7 @@ pub struct GithubAppPermissions { deployments: Option, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubApp { id: u32, slug: String, @@ -352,7 +346,7 @@ pub struct GithubApp { pem: Option, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] pub struct GithubIssue { id: u32, node_id: String, @@ -388,7 +382,7 @@ pub struct GithubIssue { title: String, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] #[serde(rename_all = "snake_case", tag = "action")] #[allow(clippy::large_enum_variant)] pub enum IssueCommentEvent { @@ -400,7 +394,7 @@ pub enum IssueCommentEvent { Edited {}, } -#[derive(Serialize, Deserialize, Debug, JsonSchema)] +#[derive(Serialize, Deserialize, Debug, ToSchema)] #[serde(rename_all = "snake_case", tag = "action")] #[allow(clippy::large_enum_variant)] pub enum IssuesEvent { @@ -618,8 +612,12 @@ pub struct Event { repository: GithubRepository, } -#[derive(Debug, JsonSchema)] -pub struct EventHeader<'r>(pub &'r str); +#[derive(Debug, IntoParams)] +#[into_params(names("X-Github-Event"), parameter_in=Header)] +pub struct EventHeader<'r>( + /// The name of the github event + pub &'r str, +); impl std::ops::Deref for EventHeader<'_> { type Target = str; @@ -646,40 +644,6 @@ impl<'r> FromRequest<'r> for EventHeader<'r> { } } -impl<'r> OpenApiFromRequest<'r> for EventHeader<'r> { - fn from_request_input( - _gen: &mut OpenApiGenerator, - _name: String, - _required: bool, - ) -> revolt_rocket_okapi::Result { - let mut content = schemars::Map::new(); - content.insert( - "X-Github-Event".to_string(), - MediaType { - schema: Some(SchemaObject { - string: Some(Box::default()), - ..Default::default() - }), - example: None, - examples: None, - encoding: schemars::Map::new(), - extensions: schemars::Map::new(), - }, - ); - - Ok(RequestHeaderInput::Parameter(Parameter { - name: "X-Github-Event".to_string(), - location: "header".to_string(), - required: true, - description: Some("The name of the github event".to_string()), - deprecated: false, - allow_empty_value: false, - value: ParameterValue::Content { content }, - extensions: schemars::Map::new(), - })) - } -} - const GREEN: &str = "#0f890f"; const RED: &str = "#e73e3e"; const GREY: &str = "#202224"; @@ -747,7 +711,7 @@ fn convert_event(data: &str, event_name: &str) -> Result { /// # Executes a webhook specific to github /// /// Executes a webhook specific to github and sends a message containing the relevant info about the event -#[openapi(tag = "Webhooks")] +#[utoipa::path(tag = "Webhooks", params(EventHeader))] #[post("///github", data = "")] pub async fn webhook_execute_github( db: &State, diff --git a/crates/delta/src/routes/webhooks/webhook_fetch.rs b/crates/delta/src/routes/webhooks/webhook_fetch.rs index c8569e17..870adc75 100644 --- a/crates/delta/src/routes/webhooks/webhook_fetch.rs +++ b/crates/delta/src/routes/webhooks/webhook_fetch.rs @@ -10,7 +10,7 @@ use rocket::{serde::json::Json, State}; /// # Gets a webhook /// /// Gets a webhook -#[openapi(tag = "Webhooks")] +#[utoipa::path(tag = "Webhooks")] #[get("/")] pub async fn webhook_fetch( db: &State, diff --git a/crates/delta/src/routes/webhooks/webhook_fetch_token.rs b/crates/delta/src/routes/webhooks/webhook_fetch_token.rs index 70c95825..f1a9a92c 100644 --- a/crates/delta/src/routes/webhooks/webhook_fetch_token.rs +++ b/crates/delta/src/routes/webhooks/webhook_fetch_token.rs @@ -6,7 +6,7 @@ use rocket::{serde::json::Json, State}; /// # Gets a webhook /// /// Gets a webhook with a token -#[openapi(tag = "Webhooks")] +#[utoipa::path(tag = "Webhooks")] #[get("//")] pub async fn webhook_fetch_token( db: &State, diff --git a/crates/services/autumn/Cargo.toml b/crates/services/autumn/Cargo.toml index e15fbf0e..70105045 100644 --- a/crates/services/autumn/Cargo.toml +++ b/crates/services/autumn/Cargo.toml @@ -47,20 +47,23 @@ revolt-files = { version = "0.8.9", path = "../../core/files" } revolt-config = { version = "0.8.9", path = "../../core/config" } revolt-database = { version = "0.8.9", path = "../../core/database", features = [ "axum-impl", + "utoipa", ] } revolt-result = { version = "0.8.9", path = "../../core/result", features = [ "utoipa", "axum", ] } -revolt-ratelimits = { version = "0.8.9", path = "../../core/ratelimits", features = ["axum"] } +revolt-ratelimits = { version = "0.8.9", path = "../../core/ratelimits", features = [ + "axum", +] } # Axum / web server tempfile = "3.12.0" -axum-macros = "0.4.1" -axum_typed_multipart = "0.12.1" -axum = { version = "0.7.5", features = ["multipart"] } +axum-macros = "0.5.0" +axum_typed_multipart = "0.16.4" +axum = { version = "0.8.6", features = ["multipart"] } tower-http = { version = "0.5.2", features = ["cors"] } # OpenAPI & documentation generation -utoipa-scalar = { version = "0.1.0", features = ["axum"] } -utoipa = { version = "4.2.3", features = ["axum_extras", "ulid"] } +utoipa-scalar = { version = "0.3.0", features = ["axum"] } +utoipa = { version = "5.4.0", features = ["ulid"] } diff --git a/crates/services/autumn/src/api.rs b/crates/services/autumn/src/api.rs index 5c567146..92f7ddb6 100644 --- a/crates/services/autumn/src/api.rs +++ b/crates/services/autumn/src/api.rs @@ -23,9 +23,14 @@ use sha2::Digest; use tempfile::NamedTempFile; use tokio::time::Instant; use tower_http::cors::{AllowHeaders, Any, CorsLayer}; -use utoipa::ToSchema; +use utoipa::{ + openapi::{schema::ObjectBuilder, SchemaFormat}, + PartialSchema, ToSchema, +}; -use crate::{exif::strip_metadata, metadata::generate_metadata, mime_type::determine_mime_type, AppState}; +use crate::{ + exif::strip_metadata, metadata::generate_metadata, mime_type::determine_mime_type, AppState, +}; /// Build the API router pub async fn router() -> Router { @@ -122,14 +127,32 @@ pub enum Tag { } /// Request body for upload -#[derive(ToSchema, TryFromMultipart)] +#[derive(TryFromMultipart)] pub struct UploadPayload { - #[schema(format = Binary)] #[allow(dead_code)] #[form_data(limit = "unlimited")] // handled by axum file: FieldData, } +impl ToSchema for UploadPayload { + fn name() -> std::borrow::Cow<'static, str> { + std::borrow::Cow::Borrowed("UploadPayload") + } +} + +impl PartialSchema for UploadPayload { + fn schema() -> utoipa::openapi::RefOr { + ObjectBuilder::new() + .property( + "file", + ObjectBuilder::new().format(Some(SchemaFormat::KnownFormat( + utoipa::openapi::KnownFormat::Binary, + ))), + ) + .into() + } +} + /// Successful upload response #[derive(Serialize, Debug, ToSchema)] pub struct UploadResponse { @@ -160,8 +183,8 @@ pub struct UploadResponse { ), request_body(content_type = "multipart/form-data", content = UploadPayload), security( - ("session_token" = []), - ("bot_token" = []) + ("Session-Token" = []), + ("Bot-Token" = []) ) )] async fn upload_file( diff --git a/crates/services/autumn/src/main.rs b/crates/services/autumn/src/main.rs index cb936e0c..b0888362 100644 --- a/crates/services/autumn/src/main.rs +++ b/crates/services/autumn/src/main.rs @@ -3,13 +3,10 @@ use std::net::{Ipv4Addr, SocketAddr}; use axum::{middleware::from_fn_with_state, Router}; use axum_macros::FromRef; -use revolt_database::{Database, DatabaseInfo}; +use revolt_database::{Database, DatabaseInfo, User, util::utoipa::TokenSecurity}; use revolt_ratelimits::axum as ratelimiter; use tokio::net::TcpListener; -use utoipa::{ - openapi::security::{ApiKey, ApiKeyValue, SecurityScheme}, - Modify, OpenApi, -}; +use utoipa::OpenApi; use utoipa_scalar::{Scalar, Servable as ScalarServable}; mod api; @@ -36,7 +33,7 @@ async fn main() -> Result<(), std::io::Error> { // Configure API schema #[derive(OpenApi)] #[openapi( - modifiers(&SecurityAddon), + modifiers(&TokenSecurity), paths( api::root, api::upload_file, @@ -59,23 +56,6 @@ async fn main() -> Result<(), std::io::Error> { )] struct ApiDoc; - struct SecurityAddon; - - impl Modify for SecurityAddon { - fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) { - if let Some(components) = openapi.components.as_mut() { - components.add_security_scheme( - "bot_token", - SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("X-Bot-Token"))), - ); - components.add_security_scheme( - "session_token", - SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("X-Session-Token"))), - ); - } - } - } - // Connect to the database let db = DatabaseInfo::Auto.connect().await.unwrap(); let ratelimits = ratelimiter::RatelimitStorage::new(ratelimits::AutumnRatelimits); diff --git a/crates/services/gifbox/Cargo.toml b/crates/services/gifbox/Cargo.toml index c611f59d..de1be55e 100644 --- a/crates/services/gifbox/Cargo.toml +++ b/crates/services/gifbox/Cargo.toml @@ -33,12 +33,12 @@ revolt-ratelimits = { version = "0.8.9", path = "../../core/ratelimits", feature ] } # Axum / web server -axum = { version = "0.7.5" } -axum-extra = { version = "0.9", features = ["typed-header"] } +axum = { version = "0.8.6" } +axum-extra = { version = "0.12.1", features = ["typed-header"] } # OpenAPI & documentation generation -utoipa-scalar = { version = "0.1.0", features = ["axum"] } -utoipa = { version = "4.2.3", features = ["axum_extras", "ulid"] } +utoipa-scalar = { version = "0.3.0", features = ["axum"] } +utoipa = { version = "5.4.0", features = ["ulid"] } # Logging tracing = "0.1" diff --git a/crates/services/gifbox/src/main.rs b/crates/services/gifbox/src/main.rs index 059cb22f..deec8fc3 100644 --- a/crates/services/gifbox/src/main.rs +++ b/crates/services/gifbox/src/main.rs @@ -1,16 +1,15 @@ use std::net::{Ipv4Addr, SocketAddr}; -use axum::{extract::FromRef, middleware::from_fn_with_state, Router}; +use axum::{ + extract::FromRef, middleware::from_fn_with_state, Router, +}; use revolt_config::config; -use revolt_database::{Database, DatabaseInfo}; +use revolt_database::{Database, DatabaseInfo, util::utoipa::TokenSecurity}; use revolt_ratelimits::axum as ratelimiter; use tokio::net::TcpListener; -use utoipa::{ - openapi::security::{ApiKey, ApiKeyValue, SecurityScheme}, - Modify, OpenApi, -}; -use utoipa_scalar::{Scalar, Servable as ScalarServable}; +use utoipa::OpenApi; +use utoipa_scalar::{Scalar, Servable}; use crate::tenor::Tenor; @@ -26,26 +25,6 @@ struct AppState { pub ratelimit_storage: ratelimiter::RatelimitStorage, } -struct SecurityAddon; - -impl Modify for SecurityAddon { - fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) { - let components = openapi.components.get_or_insert_default(); - - components.add_security_scheme( - "User Token", - SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new( - "X-Session-Token".to_string(), - ))), - ); - - components.add_security_scheme( - "Bot Token", - SecurityScheme::ApiKey(ApiKey::Header(ApiKeyValue::new("X-Bot-Token".to_string()))), - ); - } -} - #[tokio::main] async fn main() -> Result<(), std::io::Error> { // Configure logging and environment @@ -54,7 +33,7 @@ async fn main() -> Result<(), std::io::Error> { // Configure API schema #[derive(OpenApi)] #[openapi( - modifiers(&SecurityAddon), + modifiers(&TokenSecurity), paths( routes::categories::categories, routes::root::root, diff --git a/crates/services/gifbox/src/routes/categories.rs b/crates/services/gifbox/src/routes/categories.rs index a79f03c0..145686f6 100644 --- a/crates/services/gifbox/src/routes/categories.rs +++ b/crates/services/gifbox/src/routes/categories.rs @@ -3,7 +3,7 @@ use axum::{ Json, }; use revolt_database::User; -use revolt_result::{create_error, Result}; +use revolt_result::{create_error, Result, Error}; use serde::Deserialize; use utoipa::IntoParams; @@ -21,10 +21,11 @@ pub struct CategoriesQueryParams { get, path = "/categories", tag = "GIFs", - security(("User Token" = []), ("Bot Token" = [])), + security(("Session-Token" = []), ("Bot-Token" = [])), params(CategoriesQueryParams), responses( - (status = 200, description = "Categories results", body = inline(Vec)) + (status = 200, description = "Categories results", body = inline(Vec)), + (status = "default", body = Error) ) )] pub async fn categories( diff --git a/crates/services/gifbox/src/routes/search.rs b/crates/services/gifbox/src/routes/search.rs index ac541dc2..55e41e91 100644 --- a/crates/services/gifbox/src/routes/search.rs +++ b/crates/services/gifbox/src/routes/search.rs @@ -3,7 +3,7 @@ use axum::{ Json, }; use revolt_database::User; -use revolt_result::{create_error, Result}; +use revolt_result::{Error, Result, create_error}; use serde::Deserialize; use utoipa::IntoParams; @@ -30,10 +30,11 @@ pub struct SearchQueryParams { get, path = "/search", tag = "GIFs", - security(("User Token" = []), ("Bot Token" = [])), + security(("Session-Token" = []), ("Bot-Token" = [])), params(SearchQueryParams), responses( - (status = 200, description = "Search results", body = inline(types::PaginatedMediaResponse)) + (status = 200, description = "Search results", body = types::PaginatedMediaResponse), + (status = "default", body = Error) ) )] pub async fn search( diff --git a/crates/services/gifbox/src/routes/trending.rs b/crates/services/gifbox/src/routes/trending.rs index b8105524..796557ea 100644 --- a/crates/services/gifbox/src/routes/trending.rs +++ b/crates/services/gifbox/src/routes/trending.rs @@ -3,7 +3,7 @@ use axum::{ Json, }; use revolt_database::User; -use revolt_result::{create_error, Result}; +use revolt_result::{create_error, Result, Error}; use serde::Deserialize; use utoipa::IntoParams; @@ -25,10 +25,11 @@ pub struct TrendingQueryParams { get, path = "/featured", tag = "GIFs", - security(("User Token" = []), ("Bot Token" = [])), + security(("Session-Token" = []), ("Bot-Token" = [])), params(TrendingQueryParams), responses( - (status = 200, description = "Trending results", body = inline(types::PaginatedMediaResponse)) + (status = 200, description = "Trending results", body = types::PaginatedMediaResponse), + (status = "default", body = Error), ) )] pub async fn trending( diff --git a/crates/services/january/Cargo.toml b/crates/services/january/Cargo.toml index b549882a..b861f504 100644 --- a/crates/services/january/Cargo.toml +++ b/crates/services/january/Cargo.toml @@ -41,9 +41,9 @@ revolt-result = { version = "0.8.9", path = "../../core/result", features = [ revolt-files = { version = "0.8.9", path = "../../core/files" } # Axum / web server -axum = { version = "0.7.5" } -axum-extra = { version = "0.9", features = ["typed-header"] } +axum = { version = "0.8.6" } +axum-extra = { version = "0.12.0", features = ["typed-header"] } # OpenAPI & documentation generation -utoipa-scalar = { version = "0.1.0", features = ["axum"] } -utoipa = { version = "4.2.3", features = ["axum_extras", "ulid"] } +utoipa-scalar = { version = "0.3.0", features = ["axum"] } +utoipa = { version = "5.4.0", features = ["ulid"] } diff --git a/crates/services/january/src/api.rs b/crates/services/january/src/api.rs index d0772e99..bf8597ab 100644 --- a/crates/services/january/src/api.rs +++ b/crates/services/january/src/api.rs @@ -80,9 +80,6 @@ async fn proxy(Query(UrlQuery { url }): Query) -> Result, diff --git a/crates/services/january/src/main.rs b/crates/services/january/src/main.rs index b905b87d..65ca9bd4 100644 --- a/crates/services/january/src/main.rs +++ b/crates/services/january/src/main.rs @@ -3,11 +3,8 @@ use std::net::{Ipv4Addr, SocketAddr}; use axum::Router; use tokio::net::TcpListener; -use utoipa::{ - openapi::security::{Http, HttpAuthScheme, SecurityScheme}, - Modify, OpenApi, -}; -use utoipa_scalar::{Scalar, Servable as ScalarServable}; +use utoipa::OpenApi; +use utoipa_scalar::{Scalar, Servable}; mod api; pub mod requests; @@ -21,7 +18,6 @@ async fn main() -> Result<(), std::io::Error> { // Configure API schema #[derive(OpenApi)] #[openapi( - modifiers(&SecurityAddon), paths( api::root, api::proxy, @@ -47,22 +43,17 @@ async fn main() -> Result<(), std::io::Error> { )] struct ApiDoc; - struct SecurityAddon; - - impl Modify for SecurityAddon { - fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) { - if let Some(components) = openapi.components.as_mut() { - components.add_security_scheme( - "api_key", - SecurityScheme::Http(Http::new(HttpAuthScheme::Bearer)), - ) - } - } - } - // Configure Axum and router let app = Router::new() .merge(Scalar::with_url("/scalar", ApiDoc::openapi())) + // .route("/scalar", { + // let html = Scalar::new(ApiDoc::openapi()).to_html(); + + // get(move || { + // let html = html.clone(); + // async { Html(html) } + // }) + // }) .nest("/", api::router().await); // Configure TCP listener and bind