Compare commits
5 Commits
f39495fbb4
...
85ee56e21b
Author | SHA1 | Date |
---|---|---|
Natty | 85ee56e21b | |
Natty | e045d2aae4 | |
Natty | 7bffc5f16a | |
Natty | 30e3da71e9 | |
Natty | 3bf0d16e8a |
|
@ -514,7 +514,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ck"
|
name = "ck"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sea-orm",
|
"sea-orm",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -587,16 +587,6 @@ version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747"
|
checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "core-foundation"
|
|
||||||
version = "0.9.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
|
|
||||||
dependencies = [
|
|
||||||
"core-foundation-sys",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.4"
|
version = "0.8.4"
|
||||||
|
@ -755,15 +745,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "encoding_rs"
|
|
||||||
version = "0.8.32"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "equivalent"
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -840,21 +821,6 @@ version = "1.0.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foreign-types"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
|
||||||
dependencies = [
|
|
||||||
"foreign-types-shared",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "foreign-types-shared"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "form_urlencoded"
|
name = "form_urlencoded"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
@ -1225,19 +1191,6 @@ dependencies = [
|
||||||
"want",
|
"want",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper-tls"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"hyper",
|
|
||||||
"native-tls",
|
|
||||||
"tokio",
|
|
||||||
"tokio-native-tls",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "iana-time-zone"
|
name = "iana-time-zone"
|
||||||
version = "0.1.57"
|
version = "0.1.57"
|
||||||
|
@ -1334,12 +1287,6 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ipnet"
|
|
||||||
version = "2.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is-terminal"
|
name = "is-terminal"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
@ -1458,6 +1405,7 @@ dependencies = [
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1537,19 +1485,13 @@ dependencies = [
|
||||||
name = "magnetar_sdk"
|
name = "magnetar_sdk"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
|
||||||
"chrono",
|
"chrono",
|
||||||
"http",
|
"http",
|
||||||
"js-sys",
|
|
||||||
"magnetar_sdk_macros",
|
"magnetar_sdk_macros",
|
||||||
"reqwest",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde-wasm-bindgen",
|
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
"wasm-bindgen",
|
"unicode-segmentation",
|
||||||
"wasm-bindgen-futures",
|
|
||||||
"web-sys",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1664,24 +1606,6 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "native-tls"
|
|
||||||
version = "0.2.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
|
|
||||||
dependencies = [
|
|
||||||
"lazy_static",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"openssl",
|
|
||||||
"openssl-probe",
|
|
||||||
"openssl-sys",
|
|
||||||
"schannel",
|
|
||||||
"security-framework",
|
|
||||||
"security-framework-sys",
|
|
||||||
"tempfile",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.3"
|
version = "7.1.3"
|
||||||
|
@ -1786,50 +1710,6 @@ version = "1.18.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl"
|
|
||||||
version = "0.10.55"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"cfg-if",
|
|
||||||
"foreign-types",
|
|
||||||
"libc",
|
|
||||||
"once_cell",
|
|
||||||
"openssl-macros",
|
|
||||||
"openssl-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl-macros"
|
|
||||||
version = "0.1.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.28",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl-probe"
|
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl-sys"
|
|
||||||
version = "0.9.90"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"pkg-config",
|
|
||||||
"vcpkg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-float"
|
name = "ordered-float"
|
||||||
version = "3.7.0"
|
version = "3.7.0"
|
||||||
|
@ -2262,43 +2142,6 @@ dependencies = [
|
||||||
"bytecheck",
|
"bytecheck",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "reqwest"
|
|
||||||
version = "0.11.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.21.2",
|
|
||||||
"bytes",
|
|
||||||
"encoding_rs",
|
|
||||||
"futures-core",
|
|
||||||
"futures-util",
|
|
||||||
"h2",
|
|
||||||
"http",
|
|
||||||
"http-body",
|
|
||||||
"hyper",
|
|
||||||
"hyper-tls",
|
|
||||||
"ipnet",
|
|
||||||
"js-sys",
|
|
||||||
"log",
|
|
||||||
"mime",
|
|
||||||
"native-tls",
|
|
||||||
"once_cell",
|
|
||||||
"percent-encoding",
|
|
||||||
"pin-project-lite",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"serde_urlencoded",
|
|
||||||
"tokio",
|
|
||||||
"tokio-native-tls",
|
|
||||||
"tower-service",
|
|
||||||
"url",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"wasm-bindgen-futures",
|
|
||||||
"web-sys",
|
|
||||||
"winreg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.16.20"
|
version = "0.16.20"
|
||||||
|
@ -2451,15 +2294,6 @@ dependencies = [
|
||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "schannel"
|
|
||||||
version = "0.1.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
|
|
||||||
dependencies = [
|
|
||||||
"windows-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
@ -2641,29 +2475,6 @@ version = "4.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "security-framework"
|
|
||||||
version = "2.9.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"core-foundation",
|
|
||||||
"core-foundation-sys",
|
|
||||||
"libc",
|
|
||||||
"security-framework-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "security-framework-sys"
|
|
||||||
version = "2.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
|
|
||||||
dependencies = [
|
|
||||||
"core-foundation-sys",
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.180"
|
version = "1.0.180"
|
||||||
|
@ -2673,17 +2484,6 @@ dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde-wasm-bindgen"
|
|
||||||
version = "0.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e"
|
|
||||||
dependencies = [
|
|
||||||
"js-sys",
|
|
||||||
"serde",
|
|
||||||
"wasm-bindgen",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.180"
|
version = "1.0.180"
|
||||||
|
@ -3335,16 +3135,6 @@ dependencies = [
|
||||||
"syn 2.0.28",
|
"syn 2.0.28",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-native-tls"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
|
||||||
dependencies = [
|
|
||||||
"native-tls",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-stream"
|
name = "tokio-stream"
|
||||||
version = "0.1.14"
|
version = "0.1.14"
|
||||||
|
@ -3784,18 +3574,6 @@ dependencies = [
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasm-bindgen-futures"
|
|
||||||
version = "0.4.37"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"js-sys",
|
|
||||||
"wasm-bindgen",
|
|
||||||
"web-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen-macro"
|
name = "wasm-bindgen-macro"
|
||||||
version = "0.2.87"
|
version = "0.2.87"
|
||||||
|
@ -3965,15 +3743,6 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winreg"
|
|
||||||
version = "0.10.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
|
||||||
dependencies = [
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wyz"
|
name = "wyz"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|
|
@ -55,6 +55,7 @@ tower-http = "0.4"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
ts-rs = "6"
|
ts-rs = "6"
|
||||||
|
unicode-segmentation = "1.10"
|
||||||
url = "2.3"
|
url = "2.3"
|
||||||
walkdir = "2.3"
|
walkdir = "2.3"
|
||||||
wasm-bindgen = "0.2"
|
wasm-bindgen = "0.2"
|
||||||
|
@ -73,7 +74,7 @@ cached = { workspace = true }
|
||||||
chrono = { workspace = true }
|
chrono = { workspace = true }
|
||||||
dotenvy = { workspace = true }
|
dotenvy = { workspace = true }
|
||||||
|
|
||||||
axum = { workspace = true, features = ["macros"] }
|
axum = { workspace = true, features = ["macros", "headers"] }
|
||||||
headers = { workspace = true }
|
headers = { workspace = true }
|
||||||
hyper = { workspace = true, features = ["full"] }
|
hyper = { workspace = true, features = ["full"] }
|
||||||
tokio = { workspace = true, features = ["full"] }
|
tokio = { workspace = true, features = ["full"] }
|
||||||
|
@ -95,5 +96,7 @@ serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
toml = { workspace = true }
|
toml = { workspace = true }
|
||||||
|
|
||||||
|
unicode-segmentation = { workspace = true }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "ck"
|
name = "ck"
|
||||||
version = "0.1.0"
|
version.workspace = true
|
||||||
edition = "2021"
|
edition.workspace = true
|
||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
pub use ck;
|
||||||
|
use ck::*;
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
pub use ck::*;
|
|
||||||
use ext_calckey_model_migration::{Migrator, MigratorTrait};
|
use ext_calckey_model_migration::{Migrator, MigratorTrait};
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use redis::IntoConnectionInfo;
|
use redis::IntoConnectionInfo;
|
||||||
|
|
|
@ -3,44 +3,15 @@ name = "magnetar_sdk"
|
||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["rlib", "cdylib"]
|
|
||||||
|
|
||||||
[features]
|
|
||||||
reqwest = ["dep:reqwest"]
|
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.features]
|
|
||||||
default = ["reqwest"]
|
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.features]
|
|
||||||
default = []
|
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
magnetar_sdk_macros = { path = "./macros" }
|
magnetar_sdk_macros = { path = "./macros" }
|
||||||
|
|
||||||
chrono = { workspace = true, features = ["serde"] }
|
chrono = { workspace = true, features = ["serde"] }
|
||||||
reqwest = { workspace = true, features = ["json"], optional = true }
|
|
||||||
|
|
||||||
http = { workspace = true }
|
http = { workspace = true }
|
||||||
async-trait = { workspace = true }
|
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
ts-rs = { workspace = true, features = ["chrono", "chrono-impl"] }
|
ts-rs = { workspace = true, features = ["chrono", "chrono-impl"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
unicode-segmentation = { workspace = true }
|
||||||
wasm-bindgen = { workspace = true }
|
|
||||||
wasm-bindgen-futures = { workspace = true }
|
|
||||||
serde-wasm-bindgen = { workspace = true }
|
|
||||||
chrono = { workspace = true, features = ["serde", "wasm-bindgen", "js-sys"] }
|
|
||||||
js-sys = { workspace = true }
|
|
||||||
web-sys = { workspace = true, features = [
|
|
||||||
"Headers",
|
|
||||||
"Request",
|
|
||||||
"RequestInit",
|
|
||||||
"RequestMode",
|
|
||||||
"Response",
|
|
||||||
"Window",
|
|
||||||
"UrlSearchParams"
|
|
||||||
] }
|
|
|
@ -2,11 +2,11 @@ use proc_macro::TokenStream;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use syn::parse::Parse;
|
use syn::parse::Parse;
|
||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
use syn::{Expr, ExprLit, ExprPath, Ident, Lit, Meta, MetaNameValue, Token};
|
use syn::{Expr, ExprLit, ExprPath, Ident, Lit, Meta, MetaNameValue, Token, Type};
|
||||||
|
|
||||||
struct Field {
|
struct Field {
|
||||||
name: Ident,
|
name: Ident,
|
||||||
ty: Ident,
|
ty: Type,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PackInput {
|
struct PackInput {
|
||||||
|
@ -50,9 +50,17 @@ pub fn pack(item: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
let names = fields.iter().map(|f| &f.name);
|
let names = fields.iter().map(|f| &f.name);
|
||||||
let types = fields.iter().map(|f| &f.ty);
|
let types = fields.iter().map(|f| &f.ty);
|
||||||
|
let types_packed = fields.iter().map(|f| &f.ty);
|
||||||
|
|
||||||
|
let names_packed = fields.iter().map(|f| &f.name);
|
||||||
|
let iota = (0..fields.len()).map(syn::Index::from);
|
||||||
|
|
||||||
let export_path = format!("bindings/packed/{}.ts", struct_name);
|
let export_path = format!("bindings/packed/{}.ts", struct_name);
|
||||||
|
|
||||||
|
let tuple = quote::quote! {
|
||||||
|
(#(#types_packed,)*)
|
||||||
|
};
|
||||||
|
|
||||||
let expanded = quote::quote! {
|
let expanded = quote::quote! {
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export, export_to = #export_path)]
|
#[ts(export, export_to = #export_path)]
|
||||||
|
@ -62,6 +70,18 @@ pub fn pack(item: TokenStream) -> TokenStream {
|
||||||
pub #names: #types
|
pub #names: #types
|
||||||
),*
|
),*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Packed for #struct_name {
|
||||||
|
type Input = #tuple;
|
||||||
|
|
||||||
|
fn pack_from(from: #tuple) -> Self {
|
||||||
|
#struct_name {
|
||||||
|
#(
|
||||||
|
#names_packed: from.#iota
|
||||||
|
),*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TokenStream::from(expanded)
|
TokenStream::from(expanded)
|
||||||
|
@ -172,6 +192,10 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
|
||||||
let response = response.expect("missing response attribute");
|
let response = response.expect("missing response attribute");
|
||||||
|
|
||||||
let ts_path = format!("bindings/endpoints/{}.ts", name);
|
let ts_path = format!("bindings/endpoints/{}.ts", name);
|
||||||
|
let export_name = Ident::new(
|
||||||
|
&format!("export_bindings_{}", struct_name.to_string().to_lowercase()),
|
||||||
|
struct_name.span(),
|
||||||
|
);
|
||||||
|
|
||||||
let expanded = quote::quote! {
|
let expanded = quote::quote! {
|
||||||
impl Default for #struct_name {
|
impl Default for #struct_name {
|
||||||
|
@ -191,7 +215,7 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[test]
|
#[test]
|
||||||
fn export_bindings_getuserbyid() {
|
fn #export_name() {
|
||||||
#struct_name::export().expect("could not export type");
|
#struct_name::export().expect("could not export type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,10 +243,10 @@ pub fn derive_endpoint(item: TokenStream) -> TokenStream {
|
||||||
#struct_name::NAME.to_string()
|
#struct_name::NAME.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dependencies() -> Vec<Dependency> {
|
fn dependencies() -> Vec<ts_rs::Dependency> {
|
||||||
vec![
|
vec![
|
||||||
Dependency::from_ty::<<#struct_name as Endpoint>::Request>().unwrap(),
|
ts_rs::Dependency::from_ty::<<#struct_name as Endpoint>::Request>().unwrap(),
|
||||||
Dependency::from_ty::<<#struct_name as Endpoint>::Response>().unwrap(),
|
ts_rs::Dependency::from_ty::<<#struct_name as Endpoint>::Response>().unwrap(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,277 +0,0 @@
|
||||||
use crate::client::ApiClientImpl;
|
|
||||||
use crate::endpoints::list::match_endpoint;
|
|
||||||
use crate::endpoints::{Endpoint, ErrorKind, ResponseError};
|
|
||||||
use crate::Client;
|
|
||||||
use http::Method;
|
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
use serde::Serialize;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
pub struct FetchClient {
|
|
||||||
base_url: String,
|
|
||||||
application: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FetchClient {
|
|
||||||
pub fn new(base_url: &str, application: &str) -> Self {
|
|
||||||
FetchClient {
|
|
||||||
base_url: base_url.to_string(),
|
|
||||||
application: application.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
|
||||||
impl Client for FetchClient {
|
|
||||||
fn base_url(&self) -> &str {
|
|
||||||
&self.base_url
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn call<'a, I, O, E>(
|
|
||||||
&self,
|
|
||||||
endpoint: &E,
|
|
||||||
data: &I,
|
|
||||||
path_params: &impl AsRef<[(&'a str, &'a str)]>,
|
|
||||||
) -> Result<O, ResponseError>
|
|
||||||
where
|
|
||||||
I: Serialize + DeserializeOwned + Send + 'static,
|
|
||||||
O: Serialize + DeserializeOwned + Send + 'static,
|
|
||||||
E: Endpoint<Request = I, Response = O> + Send + 'static,
|
|
||||||
{
|
|
||||||
let url = endpoint.template_path(self.base_url(), path_params);
|
|
||||||
let url = if E::METHOD == Method::GET {
|
|
||||||
let search = web_sys::UrlSearchParams::new_with_str_sequence_sequence(
|
|
||||||
&serde_wasm_bindgen::to_value(data).unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
format!("{}?{}", url, search.to_string())
|
|
||||||
} else {
|
|
||||||
url
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut opts = web_sys::RequestInit::new();
|
|
||||||
opts.method(E::METHOD.as_str());
|
|
||||||
if E::METHOD != Method::GET {
|
|
||||||
opts.body(Some(&serde_wasm_bindgen::to_value(data).unwrap()));
|
|
||||||
}
|
|
||||||
let headers = web_sys::Headers::new().unwrap();
|
|
||||||
headers.set("Content-Type", "application/json").unwrap();
|
|
||||||
headers.set("User-Agent", &self.application).unwrap();
|
|
||||||
opts.headers(&headers);
|
|
||||||
|
|
||||||
let req = web_sys::Request::new_with_str_and_init(&url, &opts).unwrap();
|
|
||||||
|
|
||||||
let js_response = wasm_bindgen_futures::JsFuture::from(
|
|
||||||
web_sys::window()
|
|
||||||
.ok_or_else(|| ResponseError {
|
|
||||||
kind: ErrorKind::Other,
|
|
||||||
code: "FetchClient:NoWindow".to_string(),
|
|
||||||
message: "No window object".to_string(),
|
|
||||||
status: None,
|
|
||||||
})?
|
|
||||||
.fetch_with_request(&req),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ResponseError {
|
|
||||||
kind: ErrorKind::Other,
|
|
||||||
code: "FetchClient:FetchFail".to_string(),
|
|
||||||
message: format!("{:#?}", e),
|
|
||||||
status: None,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let response = web_sys::Response::from(js_response);
|
|
||||||
let status = response.status();
|
|
||||||
|
|
||||||
if (400..=599).contains(&status) {
|
|
||||||
let body = wasm_bindgen_futures::JsFuture::from(response.json().map_err(|e| {
|
|
||||||
ResponseError {
|
|
||||||
kind: ErrorKind::Other,
|
|
||||||
code: "FetchClient:ResponseErrorPromiseFail".to_string(),
|
|
||||||
message: format!("{:#?}", e),
|
|
||||||
status: Some(status),
|
|
||||||
}
|
|
||||||
})?)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ResponseError {
|
|
||||||
kind: ErrorKind::ApiError,
|
|
||||||
code: "FetchClient:ResponseErrorFail".to_string(),
|
|
||||||
message: format!("{:#?}", e),
|
|
||||||
status: Some(status),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let data = serde_wasm_bindgen::from_value::<ResponseError>(body).map_err(|e| {
|
|
||||||
ResponseError {
|
|
||||||
kind: ErrorKind::ApiError,
|
|
||||||
code: "FetchClient:ResponseErrorDeserializeFail".to_string(),
|
|
||||||
message: format!("{:#?}", e),
|
|
||||||
status: Some(status),
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
return Err(data);
|
|
||||||
} else if (200..=299).contains(&status) {
|
|
||||||
if status == 204 {
|
|
||||||
return if let Some(val) = E::default_response() {
|
|
||||||
Ok(val)
|
|
||||||
} else {
|
|
||||||
Err(ResponseError {
|
|
||||||
kind: ErrorKind::ApiError,
|
|
||||||
code: "FetchClient:ResponseError204".to_string(),
|
|
||||||
message: "Response is empty".to_string(),
|
|
||||||
status: Some(status),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let body = wasm_bindgen_futures::JsFuture::from(response.json().map_err(|e| {
|
|
||||||
ResponseError {
|
|
||||||
kind: ErrorKind::Other,
|
|
||||||
code: "FetchClient:ResponseJsonPromiseFail".to_string(),
|
|
||||||
message: format!("{:#?}", e),
|
|
||||||
status: None,
|
|
||||||
}
|
|
||||||
})?)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ResponseError {
|
|
||||||
kind: ErrorKind::Other,
|
|
||||||
code: "FetchClient:ResponseJsonFail".to_string(),
|
|
||||||
message: format!("{:#?}", e),
|
|
||||||
status: None,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let data = serde_wasm_bindgen::from_value::<O>(body).map_err(|e| ResponseError {
|
|
||||||
kind: ErrorKind::Other,
|
|
||||||
code: "FetchClient:ResponseErrorDeserializeFail".to_string(),
|
|
||||||
message: e.to_string(),
|
|
||||||
status: None,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
return Ok(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(ResponseError {
|
|
||||||
kind: ErrorKind::ApiError,
|
|
||||||
code: "FetchClient:ApiUnknownStatusError".to_string(),
|
|
||||||
message: response.status().to_string(),
|
|
||||||
status: Some(response.status()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen(getter_with_clone)]
|
|
||||||
pub struct ApiClientOptions {
|
|
||||||
base_url: String,
|
|
||||||
application: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub struct ApiClient {
|
|
||||||
client: ApiClientImpl,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
impl ApiClient {
|
|
||||||
#[wasm_bindgen(constructor)]
|
|
||||||
pub fn new(implementation: &str, options: ApiClientOptions) -> Option<ApiClient> {
|
|
||||||
match implementation {
|
|
||||||
"fetch" => Some(Self {
|
|
||||||
client: ApiClientImpl::Fetch(FetchClient::new(
|
|
||||||
&options.base_url,
|
|
||||||
&options.application,
|
|
||||||
)),
|
|
||||||
}),
|
|
||||||
#[cfg(feature = "reqwest")]
|
|
||||||
"reqwest" => Some(Self {
|
|
||||||
client: ApiClientImpl::Reqwest(crate::client::reqwest::ReqwestClient::new(
|
|
||||||
&options.base_url,
|
|
||||||
&options.application,
|
|
||||||
)),
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn call<I: Serialize, E: Endpoint, O: DeserializeOwned>(
|
|
||||||
&self,
|
|
||||||
endpoint: &E,
|
|
||||||
data: &I,
|
|
||||||
path_params: &[(&str, &str)],
|
|
||||||
) -> Result<O, ResponseError>
|
|
||||||
where
|
|
||||||
I: Serialize + DeserializeOwned + Send + 'static,
|
|
||||||
O: Serialize + DeserializeOwned + Send + 'static,
|
|
||||||
E: Endpoint<Request = I, Response = O> + Send + 'static,
|
|
||||||
{
|
|
||||||
match &self.client {
|
|
||||||
ApiClientImpl::Fetch(client) => {
|
|
||||||
client.call::<I, O, E>(endpoint, data, &path_params).await
|
|
||||||
}
|
|
||||||
#[cfg(feature = "reqwest")]
|
|
||||||
ApiClientImpl::Reqwest(client) => {
|
|
||||||
client.call::<I, O, E>(endpoint, data, &path_params).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen(getter_with_clone)]
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct EndpointLike {
|
|
||||||
pub path: String,
|
|
||||||
pub method: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[wasm_bindgen]
|
|
||||||
pub async fn api_call(
|
|
||||||
client: &ApiClient,
|
|
||||||
endpoint_like: &EndpointLike,
|
|
||||||
data: JsValue,
|
|
||||||
path_params: JsValue,
|
|
||||||
) -> Result<JsValue, ResponseError> {
|
|
||||||
let method = Method::try_from(endpoint_like.method.as_str()).map_err(|e| ResponseError {
|
|
||||||
kind: ErrorKind::Other,
|
|
||||||
code: "Bindgen::ParseMethod".to_string(),
|
|
||||||
message: e.to_string(),
|
|
||||||
status: None,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let endpoint = match_endpoint(&endpoint_like.path, &method).ok_or_else(|| ResponseError {
|
|
||||||
kind: ErrorKind::Other,
|
|
||||||
code: "Bindgen::MatchEndpoint".to_string(),
|
|
||||||
message: format!(
|
|
||||||
"No endpoint `{}` with method `{}` found",
|
|
||||||
endpoint_like.path, method
|
|
||||||
),
|
|
||||||
status: None,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let input = serde_wasm_bindgen::from_value(data).map_err(|e| ResponseError {
|
|
||||||
kind: ErrorKind::Other,
|
|
||||||
code: "Bindgen::DeserializeInput".to_string(),
|
|
||||||
message: e.to_string(),
|
|
||||||
status: None,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let path_params = serde_wasm_bindgen::from_value::<HashMap<String, String>>(path_params)
|
|
||||||
.map_err(|e| ResponseError {
|
|
||||||
kind: ErrorKind::Other,
|
|
||||||
code: "Bindgen::DeserializePathParams".to_string(),
|
|
||||||
message: e.to_string(),
|
|
||||||
status: None,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let path_params_borrow = path_params
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| (k.as_str(), v.as_str()))
|
|
||||||
.collect::<Vec<(&str, &str)>>();
|
|
||||||
|
|
||||||
serde_wasm_bindgen::to_value(&client.call(&endpoint, &input, &path_params_borrow).await?)
|
|
||||||
.map_err(|e| ResponseError {
|
|
||||||
kind: ErrorKind::Other,
|
|
||||||
code: "Bindgen::SerializeOutput".to_string(),
|
|
||||||
message: e.to_string(),
|
|
||||||
status: None,
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
#[cfg(feature = "reqwest")]
|
|
||||||
pub mod reqwest;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
pub mod fetch;
|
|
||||||
|
|
||||||
pub enum ApiClientImpl {
|
|
||||||
#[cfg(feature = "reqwest")]
|
|
||||||
Reqwest(reqwest::ReqwestClient),
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
Fetch(fetch::FetchClient),
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
use crate::endpoints::{Endpoint, ErrorKind, ResponseError};
|
|
||||||
use crate::Client;
|
|
||||||
use http::header::{CONTENT_TYPE, USER_AGENT};
|
|
||||||
use http::{HeaderMap, HeaderValue, Method, StatusCode};
|
|
||||||
use serde::de::DeserializeOwned;
|
|
||||||
use serde::Serialize;
|
|
||||||
|
|
||||||
pub struct ReqwestClient {
|
|
||||||
client: reqwest::Client,
|
|
||||||
base_url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ReqwestClient {
|
|
||||||
pub fn new(base_url: &str, application: &str) -> Self {
|
|
||||||
let mut headers = HeaderMap::new();
|
|
||||||
headers.insert(
|
|
||||||
CONTENT_TYPE,
|
|
||||||
HeaderValue::from_str("application/json").unwrap(),
|
|
||||||
);
|
|
||||||
headers.insert(USER_AGENT, HeaderValue::from_str(application).unwrap());
|
|
||||||
|
|
||||||
let client_builder = reqwest::ClientBuilder::new().default_headers(headers);
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let client_builder = { client_builder.https_only(true) };
|
|
||||||
|
|
||||||
let client = client_builder.build().unwrap();
|
|
||||||
|
|
||||||
ReqwestClient {
|
|
||||||
client,
|
|
||||||
base_url: base_url.to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
|
||||||
impl Client for ReqwestClient {
|
|
||||||
fn base_url(&self) -> &str {
|
|
||||||
&self.base_url
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn call<'a, I, O, E>(
|
|
||||||
&self,
|
|
||||||
endpoint: &E,
|
|
||||||
data: &I,
|
|
||||||
path_params: &impl AsRef<[(&'a str, &'a str)]>,
|
|
||||||
) -> Result<O, ResponseError>
|
|
||||||
where
|
|
||||||
I: Serialize + DeserializeOwned + Send + 'static,
|
|
||||||
O: Serialize + DeserializeOwned + Send + 'static,
|
|
||||||
E: Endpoint<Request = I, Response = O> + Send + 'static,
|
|
||||||
{
|
|
||||||
let url = endpoint.template_path(self.base_url(), path_params);
|
|
||||||
|
|
||||||
let req = self.client.request(E::METHOD, &url);
|
|
||||||
|
|
||||||
let req = if E::METHOD == Method::GET {
|
|
||||||
req.query(&data)
|
|
||||||
} else {
|
|
||||||
req.json(&data)
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = req.send().await.map_err(|e| ResponseError {
|
|
||||||
kind: ErrorKind::Other,
|
|
||||||
code: "ReqwestClient:Fail".to_string(),
|
|
||||||
message: e.to_string(),
|
|
||||||
status: None,
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let status = response.status();
|
|
||||||
if status.is_client_error() || status.is_server_error() {
|
|
||||||
match response.json::<ResponseError>().await {
|
|
||||||
Ok(res) => Err(res),
|
|
||||||
Err(e) => Err(ResponseError {
|
|
||||||
kind: ErrorKind::ApiError,
|
|
||||||
code: "ReqwestClient:ApiGenericError".to_string(),
|
|
||||||
message: e.to_string(),
|
|
||||||
status: Some(status.as_u16()),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
} else if status.is_success() {
|
|
||||||
if status == StatusCode::NO_CONTENT.as_u16() {
|
|
||||||
return if let Some(val) = E::default_response() {
|
|
||||||
Ok(val)
|
|
||||||
} else {
|
|
||||||
Err(ResponseError {
|
|
||||||
kind: ErrorKind::ApiError,
|
|
||||||
code: "ReqwestClient:ResponseError204".to_string(),
|
|
||||||
message: "Response is empty".to_string(),
|
|
||||||
status: Some(status.as_u16()),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = response.json::<O>().await.map_err(|e| ResponseError {
|
|
||||||
kind: ErrorKind::ApiError,
|
|
||||||
code: "ReqwestClient:JsonError".to_string(),
|
|
||||||
message: e.to_string(),
|
|
||||||
status: Some(status.as_u16()),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(data)
|
|
||||||
} else {
|
|
||||||
Err(ResponseError {
|
|
||||||
kind: ErrorKind::ApiError,
|
|
||||||
code: "ReqwestClient:ApiUnknownStatusError".to_string(),
|
|
||||||
message: status.to_string(),
|
|
||||||
status: Some(status.as_u16()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
use crate::endpoints::Endpoint;
|
|
||||||
|
|
||||||
macro_rules! match_from {
|
|
||||||
(($endpoint:expr, $method:expr) in [$($e:ty,)*]) => {
|
|
||||||
match ($endpoint, $method) {
|
|
||||||
$(
|
|
||||||
(<$e as Endpoint>::ENDPOINT, &<$e as Endpoint>::METHOD) => Some(<$e as Default>::default()),
|
|
||||||
)*
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn match_endpoint(endpoint: &str, method: &http::Method) -> Option<impl Endpoint> {
|
|
||||||
match_from!(
|
|
||||||
(endpoint, method)
|
|
||||||
in [
|
|
||||||
crate::endpoints::user::GetUserById,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
pub(crate) mod list;
|
pub mod timeline;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
||||||
use http::Method;
|
use http::Method;
|
||||||
|
@ -6,10 +6,6 @@ use serde::de::DeserializeOwned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
use wasm_bindgen::prelude::*;
|
|
||||||
|
|
||||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(getter_with_clone))]
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, TS)]
|
#[derive(Clone, Debug, Serialize, Deserialize, TS)]
|
||||||
pub struct ResponseError {
|
pub struct ResponseError {
|
||||||
pub kind: ErrorKind,
|
pub kind: ErrorKind,
|
||||||
|
@ -18,7 +14,6 @@ pub struct ResponseError {
|
||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
|
|
||||||
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, TS)]
|
#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, TS)]
|
||||||
pub enum ErrorKind {
|
pub enum ErrorKind {
|
||||||
#[default]
|
#[default]
|
||||||
|
@ -40,18 +35,7 @@ pub trait Endpoint {
|
||||||
|
|
||||||
type Request: Serialize + DeserializeOwned + Send + Sync + 'static;
|
type Request: Serialize + DeserializeOwned + Send + Sync + 'static;
|
||||||
type Response: Serialize + DeserializeOwned + Send + Sync + 'static;
|
type Response: Serialize + DeserializeOwned + Send + Sync + 'static;
|
||||||
|
|
||||||
fn default_response() -> Option<Self::Response> {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn template_path<'a>(&self, base_path: &str, var: &impl AsRef<[(&'a str, &'a str)]>) -> String {
|
pub type Req<T> = <T as Endpoint>::Request;
|
||||||
let mut path_suffix = Self::ENDPOINT.to_string();
|
pub type Res<T> = <T as Endpoint>::Response;
|
||||||
|
|
||||||
for (key, value) in var.as_ref() {
|
|
||||||
path_suffix = path_suffix.replace(&format!(":{}", key), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
format!("{}{}", base_path, path_suffix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
use crate::endpoints::Endpoint;
|
||||||
|
use crate::types::note::{NoteListFilter, PackNoteFull};
|
||||||
|
use crate::util_types::U64Range;
|
||||||
|
use http::Method;
|
||||||
|
use magnetar_sdk_macros::Endpoint;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
// Get timeline notes
|
||||||
|
#[derive(Serialize, Deserialize, TS)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct GetTimelineReq {
|
||||||
|
#[serde(default = "default_timeline_limit")]
|
||||||
|
pub limit: U64Range<1, 100>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub filter: Option<NoteListFilter>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_timeline_limit<const MIN: u64, const MAX: u64>() -> U64Range<MIN, MAX> {
|
||||||
|
15.try_into().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, TS)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct GetTimelineRes(pub Vec<PackNoteFull>);
|
||||||
|
|
||||||
|
#[derive(Endpoint)]
|
||||||
|
#[endpoint(
|
||||||
|
endpoint = "/timeline",
|
||||||
|
method = Method::GET,
|
||||||
|
request = GetTimelineReq,
|
||||||
|
response = Vec::<PackNoteFull>,
|
||||||
|
)]
|
||||||
|
pub struct GetTimeline;
|
|
@ -2,23 +2,54 @@ use crate::endpoints::Endpoint;
|
||||||
use http::Method;
|
use http::Method;
|
||||||
use magnetar_sdk_macros::Endpoint;
|
use magnetar_sdk_macros::Endpoint;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ts_rs::{Dependency, TS};
|
use ts_rs::TS;
|
||||||
|
|
||||||
use crate::types::user::PackUserProfileFull;
|
use crate::types::user::{PackUserMaybeAll, PackUserSelfMaybeAll};
|
||||||
|
|
||||||
|
// Get self
|
||||||
|
#[derive(Serialize, Deserialize, TS)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct UserSelfReq {
|
||||||
|
#[serde(default)]
|
||||||
|
pub profile: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub pins: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub detail: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub secrets: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Endpoint)]
|
||||||
|
#[endpoint(
|
||||||
|
endpoint = "/users/@self/overview/info",
|
||||||
|
method = Method::GET,
|
||||||
|
request = UserSelfReq,
|
||||||
|
response = PackUserSelfMaybeAll
|
||||||
|
)]
|
||||||
|
pub struct GetUserSelf;
|
||||||
|
|
||||||
|
// Get user by id
|
||||||
#[derive(Serialize, Deserialize, TS)]
|
#[derive(Serialize, Deserialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct UserByIdReq {
|
pub struct UserByIdReq {
|
||||||
id: String,
|
#[serde(default)]
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
pub profile: bool,
|
||||||
detail: Option<String>,
|
#[serde(default)]
|
||||||
|
pub pins: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub detail: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub relation: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub auth: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Endpoint, Deserialize)]
|
#[derive(Endpoint)]
|
||||||
#[endpoint(
|
#[endpoint(
|
||||||
endpoint = "/users/:user_id",
|
endpoint = "/users/:user_id/info",
|
||||||
method = Method::GET,
|
method = Method::GET,
|
||||||
request = UserByIdReq,
|
request = UserByIdReq,
|
||||||
response = PackUserProfileFull
|
response = PackUserMaybeAll
|
||||||
)]
|
)]
|
||||||
pub struct GetUserById;
|
pub struct GetUserById;
|
||||||
|
|
|
@ -1,23 +1,16 @@
|
||||||
pub mod client;
|
use serde::{Deserialize, Serialize};
|
||||||
|
use ts_rs::TS;
|
||||||
|
|
||||||
pub mod endpoints;
|
pub mod endpoints;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
pub mod util_types;
|
||||||
|
|
||||||
use crate::endpoints::Endpoint;
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, TS)]
|
||||||
use endpoints::ResponseError;
|
#[repr(transparent)]
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
pub struct Required<T>(pub T);
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
pub trait Packed: 'static {
|
||||||
pub trait Client {
|
type Input: 'static;
|
||||||
fn base_url(&self) -> &str;
|
|
||||||
|
|
||||||
async fn call<'a, I, O, E>(
|
fn pack_from(val: Self::Input) -> Self;
|
||||||
&self,
|
|
||||||
endpoint: &E,
|
|
||||||
data: &I,
|
|
||||||
path_params: &impl AsRef<[(&'a str, &'a str)]>,
|
|
||||||
) -> Result<O, ResponseError>
|
|
||||||
where
|
|
||||||
I: Serialize + DeserializeOwned + Send + 'static,
|
|
||||||
O: Serialize + DeserializeOwned + Send + 'static,
|
|
||||||
E: Endpoint<Request = I, Response = O> + Send + 'static;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::{Packed, Required};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
@ -8,7 +9,7 @@ use magnetar_sdk_macros::pack;
|
||||||
|
|
||||||
use crate::types::Id;
|
use crate::types::Id;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct ImageMeta {
|
pub struct ImageMeta {
|
||||||
pub width: Option<u64>,
|
pub width: Option<u64>,
|
||||||
|
@ -29,7 +30,7 @@ pub struct DriveFolderBase {
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(PackDriveFolderBase, Id as id & DriveFolderBase as folder);
|
pack!(PackDriveFolderBase, Required<Id> as id & Required<DriveFolderBase> as folder);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
|
@ -39,7 +40,7 @@ pub struct DriveFolderParentExt {
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
PackDriveFolderWithParent,
|
PackDriveFolderWithParent,
|
||||||
Id as id & DriveFolderBase as folder & DriveFolderParentExt as parent
|
Required<Id> as id & Required<DriveFolderBase> as folder & Required<DriveFolderParentExt> as parent
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
@ -48,18 +49,18 @@ pub struct DriveFileBase {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
pub sha256: String,
|
pub hash: Option<String>,
|
||||||
pub mime_type: String,
|
pub mime_type: String,
|
||||||
pub image_meta: ImageMeta,
|
pub media_metadata: ImageMeta,
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
pub thumbnail_url: Option<String>,
|
pub thumbnail_url: Option<String>,
|
||||||
pub sensitive: bool,
|
pub sensitive: bool,
|
||||||
pub comment: Option<String>,
|
pub comment: Option<String>,
|
||||||
pub folder_id: Option<String>,
|
pub folder_id: Option<String>,
|
||||||
pub user_id: String,
|
pub user_id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(PackDriveFileBase, Id as id & DriveFileBase as file);
|
pack!(PackDriveFileBase, Required<Id> as id & Required<DriveFileBase> as file);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
|
@ -69,7 +70,7 @@ pub struct DriveFileFolderExt {
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
PackDriveFileWithFolder,
|
PackDriveFileWithFolder,
|
||||||
Id as id & DriveFileBase as file & DriveFileFolderExt as folder
|
Required<Id> as id & Required<DriveFileBase> as file & Required<DriveFileFolderExt> as folder
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
@ -80,10 +81,10 @@ pub struct DriveFileUserExt {
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
PackDriveFileWithUser,
|
PackDriveFileWithUser,
|
||||||
Id as id & DriveFileBase as file & DriveFileUserExt as user
|
Required<Id> as id & Required<DriveFileBase> as file & Required<DriveFileUserExt> as user
|
||||||
);
|
);
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
PackDriveFileFull,
|
PackDriveFileFull,
|
||||||
Id as id & DriveFileBase as file & DriveFileFolderExt as folder & DriveFileUserExt as user
|
Required<Id> as id & Required<DriveFileBase> as file & Required<DriveFileFolderExt> as folder & Required<DriveFileUserExt> as user
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
use crate::types::Id;
|
use crate::types::Id;
|
||||||
|
use crate::{Packed, Required};
|
||||||
use magnetar_sdk_macros::pack;
|
use magnetar_sdk_macros::pack;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
@ -9,14 +10,14 @@ use magnetar_sdk_macros::pack;
|
||||||
pub struct EmojiBase {
|
pub struct EmojiBase {
|
||||||
pub shortcode: String,
|
pub shortcode: String,
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub static_url: String,
|
|
||||||
pub visible_in_picker: bool,
|
|
||||||
pub category: Option<String>,
|
pub category: Option<String>,
|
||||||
|
pub width: Option<i32>,
|
||||||
|
pub height: Option<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(PackEmojiBase, Id as id & EmojiBase as emoji);
|
pack!(PackEmojiBase, Required<Id> as id & Required<EmojiBase> as emoji);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct EmojiContext(pub Vec<String>);
|
pub struct EmojiContext(pub Vec<PackEmojiBase>);
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
pub mod drive;
|
pub mod drive;
|
||||||
pub mod emoji;
|
pub mod emoji;
|
||||||
pub mod note;
|
pub mod note;
|
||||||
|
pub mod timeline;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::ops::RangeInclusive;
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
pub enum RangeFilter {
|
||||||
|
TimeRange(RangeInclusive<DateTime<Utc>>),
|
||||||
|
TimeStart(DateTime<Utc>),
|
||||||
|
TimeEnd(DateTime<Utc>),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
@ -13,6 +23,12 @@ pub struct Id {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&str> for Id {
|
||||||
|
fn from(id: &str) -> Self {
|
||||||
|
Self { id: id.to_string() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Copy, Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub enum NotificationType {
|
pub enum NotificationType {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::types::emoji::EmojiContext;
|
use crate::types::emoji::EmojiContext;
|
||||||
use crate::types::user::{PackUserBase, UserBase};
|
use crate::types::user::PackUserBase;
|
||||||
use crate::types::Id;
|
use crate::types::Id;
|
||||||
|
use crate::{Packed, Required};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
@ -9,13 +10,21 @@ use crate::types::drive::PackDriveFileBase;
|
||||||
|
|
||||||
use magnetar_sdk_macros::pack;
|
use magnetar_sdk_macros::pack;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Default, Debug, Deserialize, Serialize, TS)]
|
||||||
|
#[ts(export)]
|
||||||
|
pub struct NoteListFilter {
|
||||||
|
show_renotes: Option<bool>,
|
||||||
|
show_replies: Option<bool>,
|
||||||
|
show_files_only: Option<bool>,
|
||||||
|
uncwed_sensitive: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Copy, Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub enum NoteVisibility {
|
pub enum NoteVisibility {
|
||||||
Public,
|
Public,
|
||||||
Home,
|
Home,
|
||||||
Followers,
|
Followers,
|
||||||
Specified,
|
|
||||||
Direct,
|
Direct,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +45,7 @@ pub struct PollBase {
|
||||||
pub options: Vec<PollChoice>,
|
pub options: Vec<PollChoice>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(PackPollBase, Id as id & PollBase as poll);
|
pack!(PackPollBase, Required<Id> as id & Required<PollBase> as poll);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
|
@ -47,7 +56,7 @@ pub struct NoteBase {
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
pub visibility: NoteVisibility,
|
pub visibility: NoteVisibility,
|
||||||
pub user: Box<UserBase>,
|
pub user: Box<PackUserBase>,
|
||||||
pub parent_note_id: Option<String>,
|
pub parent_note_id: Option<String>,
|
||||||
pub renoted_note_id: Option<String>,
|
pub renoted_note_id: Option<String>,
|
||||||
pub reply_count: u64,
|
pub reply_count: u64,
|
||||||
|
@ -56,41 +65,60 @@ pub struct NoteBase {
|
||||||
pub reactions: Vec<PackReactionBase>,
|
pub reactions: Vec<PackReactionBase>,
|
||||||
pub emojis: EmojiContext,
|
pub emojis: EmojiContext,
|
||||||
pub local_only: bool,
|
pub local_only: bool,
|
||||||
|
pub has_poll: bool,
|
||||||
|
pub file_ids: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(PackNoteBase, Id as id & NoteBase as note);
|
pack!(PackNoteBase, Required<Id> as id & Required<NoteBase> as note);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct NoteAttachmentExt {
|
pub struct NoteAttachmentExt {
|
||||||
pub attachments: Vec<PackDriveFileBase>,
|
pub attachments: Vec<PackDriveFileBase>,
|
||||||
pub poll: Option<PollBase>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
PackNoteWithAttachments,
|
PackNoteWithAttachments,
|
||||||
Id as id & NoteBase as note & NoteAttachmentExt as attachment
|
Required<Id> as id & Required<NoteBase> as note & Required<NoteAttachmentExt> as attachment
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
pub struct NoteDetailExt {
|
pub struct NoteDetailExt {
|
||||||
pub poll: Option<PollBase>,
|
pub poll: Option<PackPollBase>,
|
||||||
pub parent_note: Option<Box<NoteBase>>,
|
pub parent_note: Option<Box<NoteBase>>,
|
||||||
pub renoted_note: Option<Box<NoteBase>>,
|
pub renoted_note: Option<Box<NoteBase>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
PackNoteFull,
|
PackNoteFull,
|
||||||
Id as id & NoteBase as note & NoteAttachmentExt as attachment & NoteDetailExt as detail
|
Required<Id> as id & Required<NoteBase> as note & Required<NoteAttachmentExt> as attachment & Required<NoteDetailExt> as detail
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
|
pub enum Reaction {
|
||||||
|
Unicode(String),
|
||||||
|
Shortcode(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Reaction {
|
||||||
|
pub fn guess_from(s: &str, default_emote: &str) -> Option<Self> {
|
||||||
|
let code = s.trim();
|
||||||
|
match code.chars().next() {
|
||||||
|
Some(':') => Some(Reaction::Shortcode(code.to_string())),
|
||||||
|
Some(_) => Some(Reaction::Unicode(code.to_string())),
|
||||||
|
None => Self::guess_from(default_emote, "👍"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
pub struct ReactionBase {
|
pub struct ReactionBase {
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
pub user_id: String,
|
pub user_id: String,
|
||||||
|
pub reaction: Reaction,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(PackReactionBase, Id as id & ReactionBase as reaction);
|
pack!(PackReactionBase, Required<Id> as id & Required<ReactionBase> as reaction);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
pub struct ReactionUserExt {
|
pub struct ReactionUserExt {
|
||||||
|
@ -99,5 +127,5 @@ pub struct ReactionUserExt {
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
PackReactionWithUser,
|
PackReactionWithUser,
|
||||||
Id as id & ReactionBase as reaction & ReactionUserExt as user
|
Required<Id> as id & Required<ReactionBase> as reaction & Required<ReactionUserExt> as user
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)]
|
||||||
|
pub enum TimelineType {
|
||||||
|
Home,
|
||||||
|
Timeline,
|
||||||
|
Recommended,
|
||||||
|
Hybrid,
|
||||||
|
Global,
|
||||||
|
}
|
|
@ -1,22 +1,25 @@
|
||||||
use crate::types::emoji::EmojiContext;
|
use crate::types::emoji::EmojiContext;
|
||||||
use crate::types::note::PackNoteBase;
|
|
||||||
use crate::types::{Id, NotificationSettings};
|
use crate::types::{Id, NotificationSettings};
|
||||||
|
use crate::{Packed, Required};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
use crate::types::note::PackNoteFull;
|
||||||
use magnetar_sdk_macros::pack;
|
use magnetar_sdk_macros::pack;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub enum AvatarDecoration {
|
pub enum AvatarDecoration {
|
||||||
|
#[default]
|
||||||
None,
|
None,
|
||||||
CatEars,
|
CatEars,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub enum SpeechTransform {
|
pub enum SpeechTransform {
|
||||||
|
#[default]
|
||||||
None,
|
None,
|
||||||
Cat,
|
Cat,
|
||||||
}
|
}
|
||||||
|
@ -42,29 +45,29 @@ pub struct UserBase {
|
||||||
pub avatar_blurhash: Option<String>,
|
pub avatar_blurhash: Option<String>,
|
||||||
pub avatar_color: Option<String>,
|
pub avatar_color: Option<String>,
|
||||||
pub avatar_decoration: AvatarDecoration,
|
pub avatar_decoration: AvatarDecoration,
|
||||||
pub admin: bool,
|
pub is_admin: bool,
|
||||||
pub moderator: bool,
|
pub is_moderator: bool,
|
||||||
pub bot: bool,
|
pub is_bot: bool,
|
||||||
pub emojis: EmojiContext,
|
pub emojis: EmojiContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(PackUserBase, Id as id & UserBase as user);
|
pack!(PackUserBase, Required<Id> as id & Required<UserBase> as user);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct UserProfileExt {
|
pub struct UserProfileExt {
|
||||||
pub locked: bool,
|
pub is_locked: bool,
|
||||||
pub silenced: bool,
|
pub is_silenced: bool,
|
||||||
pub suspended: bool,
|
pub is_suspended: bool,
|
||||||
|
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub location: Option<String>,
|
pub location: Option<String>,
|
||||||
pub birthday: Option<DateTime<Utc>>,
|
pub birthday: Option<DateTime<Utc>>,
|
||||||
pub fields: Vec<ProfileField>,
|
pub fields: Vec<ProfileField>,
|
||||||
|
|
||||||
pub follower_count: u64,
|
pub follower_count: Option<u64>,
|
||||||
pub following_count: u64,
|
pub following_count: Option<u64>,
|
||||||
pub note_count: u64,
|
pub note_count: Option<u64>,
|
||||||
|
|
||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
|
|
||||||
|
@ -75,34 +78,19 @@ pub struct UserProfileExt {
|
||||||
pub banner_color: Option<String>,
|
pub banner_color: Option<String>,
|
||||||
pub banner_blurhash: Option<String>,
|
pub banner_blurhash: Option<String>,
|
||||||
|
|
||||||
pub public_reactions: bool,
|
pub has_public_reactions: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(
|
|
||||||
PackUserProfile,
|
|
||||||
Id as id & UserBase as user & UserProfileExt as profile
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct UserProfilePinsEx {
|
pub struct UserProfilePinsEx {
|
||||||
pub pinned_notes: Vec<PackNoteBase>,
|
pub pinned_notes: Vec<PackNoteFull>,
|
||||||
// pub pinned_page: Option<Page>,
|
// pub pinned_page: Option<Page>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(
|
|
||||||
PackUserProfileFull,
|
|
||||||
Id as id
|
|
||||||
& UserBase as user
|
|
||||||
& UserProfileExt as profile
|
|
||||||
& UserProfilePinsEx as pins
|
|
||||||
& UserRelationExt as relation
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct UserRelationExt {
|
pub struct UserRelationExt {
|
||||||
pub last_fetched_at: Option<DateTime<Utc>>,
|
|
||||||
pub follows_you: bool,
|
pub follows_you: bool,
|
||||||
pub you_follow: bool,
|
pub you_follow: bool,
|
||||||
pub blocks_you: bool,
|
pub blocks_you: bool,
|
||||||
|
@ -111,28 +99,14 @@ pub struct UserRelationExt {
|
||||||
pub mute_renotes: bool,
|
pub mute_renotes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(
|
|
||||||
PackUserRelation,
|
|
||||||
Id as id & UserBase as user & UserRelationExt as relation
|
|
||||||
);
|
|
||||||
pack!(
|
|
||||||
PackUserProfileRelation,
|
|
||||||
Id as id & UserBase as user & UserProfileExt as profile & UserRelationExt as relation
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct UserAuthOverviewExt {
|
pub struct UserAuthOverviewExt {
|
||||||
pub two_factor_enabled: bool,
|
pub has_two_factor_enabled: bool,
|
||||||
pub use_passwordless_login: bool,
|
pub has_passwordless_login: bool,
|
||||||
pub security_keys: bool,
|
pub has_security_keys: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(
|
|
||||||
PackUserAuthOverview,
|
|
||||||
Id as id & UserBase as user & UserAuthOverviewExt as auth
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct UserSelfExt {
|
pub struct UserSelfExt {
|
||||||
|
@ -154,12 +128,13 @@ pub struct UserSelfExt {
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
PackUserSelf,
|
PackUserSelf,
|
||||||
Id as id & UserBase as user & UserSelfExt as self_info
|
Required<Id> as id & Required<UserBase> as user & Required<UserSelfExt> as self_info
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct UserDetailExt {
|
pub struct UserDetailExt {
|
||||||
|
pub last_fetched_at: Option<DateTime<Utc>>,
|
||||||
pub uri: Option<String>,
|
pub uri: Option<String>,
|
||||||
pub updated_at: Option<DateTime<Utc>>,
|
pub updated_at: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
@ -168,20 +143,36 @@ pub struct UserDetailExt {
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct SecurityKeyBase {
|
pub struct SecurityKeyBase {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub created_at: DateTime<Utc>,
|
|
||||||
pub last_used_at: Option<DateTime<Utc>>,
|
pub last_used_at: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(PackSecurityKeyBase, Id as id & SecurityKeyBase as key);
|
pack!(PackSecurityKeyBase, Required<Id> as id & Required<SecurityKeyBase> as key);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
#[derive(Clone, Debug, Deserialize, Serialize, TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct UserSecretsExt {
|
pub struct UserSecretsExt {
|
||||||
pub email: Option<String>,
|
pub email: Option<String>,
|
||||||
pub email_verified: bool,
|
pub email_verified: bool,
|
||||||
pub security_keys: Vec<SecurityKeyBase>,
|
pub security_keys: Vec<PackSecurityKeyBase>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pack!(
|
pack!(
|
||||||
PackUserSelfAll,
|
PackUserMaybeAll,
|
||||||
Id as id & UserBase as user & UserSelfExt as self_info & UserSecretsExt as secrets
|
Required<Id> as id
|
||||||
|
& Required<UserBase> as user
|
||||||
|
& Option<UserProfileExt> as profile
|
||||||
|
& Option<UserProfilePinsEx> as pins
|
||||||
|
& Option<UserDetailExt> as detail
|
||||||
|
& Option<UserRelationExt> as relation
|
||||||
|
& Option<UserAuthOverviewExt> as auth
|
||||||
|
);
|
||||||
|
|
||||||
|
pack!(
|
||||||
|
PackUserSelfMaybeAll,
|
||||||
|
Required<Id> as id
|
||||||
|
& Required<UserBase> as user
|
||||||
|
& Option<UserProfileExt> as profile
|
||||||
|
& Option<UserProfilePinsEx> as pins
|
||||||
|
& Option<UserDetailExt> as detail
|
||||||
|
& Option<UserSecretsExt> as secrets
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
use serde::Serialize;
|
||||||
|
use ts_rs::TS;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct U64Range<const MIN: u64, const MAX: u64>(u64);
|
||||||
|
|
||||||
|
impl<const MIN: u64, const MAX: u64> TS for U64Range<MIN, MAX> {
|
||||||
|
const EXPORT_TO: Option<&'static str> = Some("bindings/util/u64_range.ts");
|
||||||
|
|
||||||
|
fn decl() -> String {
|
||||||
|
<u64 as TS>::decl()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name() -> String {
|
||||||
|
<u64 as TS>::name()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dependencies() -> Vec<ts_rs::Dependency> {
|
||||||
|
vec![ts_rs::Dependency::from_ty::<u64>().unwrap()]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transparent() -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const MIN: u64, const MAX: u64> Serialize for U64Range<MIN, MAX> {
|
||||||
|
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
serializer.serialize_u64(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de, const MIN: u64, const MAX: u64> serde::Deserialize<'de> for U64Range<MIN, MAX> {
|
||||||
|
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||||
|
let v = u64::deserialize(deserializer)?;
|
||||||
|
if v < MIN || v > MAX {
|
||||||
|
return Err(serde::de::Error::custom("out of range"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(U64Range(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const MIN: u64, const MAX: u64> std::ops::Deref for U64Range<MIN, MAX> {
|
||||||
|
type Target = u64;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const MIN: u64, const MAX: u64> From<U64Range<MIN, MAX>> for u64 {
|
||||||
|
fn from(v: U64Range<MIN, MAX>) -> Self {
|
||||||
|
v.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<const MIN: u64, const MAX: u64> std::fmt::Display for U64Range<MIN, MAX> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
self.0.fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct OutOfRange;
|
||||||
|
|
||||||
|
impl<const MIN: u64, const MAX: u64> TryFrom<u64> for U64Range<MIN, MAX> {
|
||||||
|
type Error = OutOfRange;
|
||||||
|
|
||||||
|
fn try_from(value: u64) -> Result<Self, Self::Error> {
|
||||||
|
if value < MIN || value > MAX {
|
||||||
|
return Err(OutOfRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(U64Range(value))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
mod user;
|
mod user;
|
||||||
|
|
||||||
use crate::api_v1::user::handle_user_info;
|
use crate::api_v1::user::{handle_user_info, handle_user_info_self};
|
||||||
use crate::service::MagnetarService;
|
use crate::service::MagnetarService;
|
||||||
use crate::web::auth;
|
use crate::web::auth;
|
||||||
use crate::web::auth::AuthState;
|
use crate::web::auth::AuthState;
|
||||||
|
@ -11,7 +11,8 @@ use std::sync::Arc;
|
||||||
|
|
||||||
pub fn create_api_router(service: Arc<MagnetarService>) -> Router {
|
pub fn create_api_router(service: Arc<MagnetarService>) -> Router {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/user/@self", get(handle_user_info))
|
.route("/users/@self", get(handle_user_info_self))
|
||||||
|
.route("/users/:id", get(handle_user_info))
|
||||||
.layer(from_fn_with_state(
|
.layer(from_fn_with_state(
|
||||||
AuthState::new(service.clone()),
|
AuthState::new(service.clone()),
|
||||||
auth::auth,
|
auth::auth,
|
||||||
|
|
|
@ -1,18 +1,36 @@
|
||||||
use crate::service::MagnetarService;
|
use crate::service::MagnetarService;
|
||||||
use crate::web::auth::AuthenticatedUser;
|
use crate::web::auth::{AuthenticatedUser, MaybeUser};
|
||||||
use crate::web::ApiError;
|
use crate::web::ApiError;
|
||||||
use axum::extract::State;
|
use axum::extract::{Path, Query, State};
|
||||||
use axum::response::IntoResponse;
|
|
||||||
use axum::Json;
|
use axum::Json;
|
||||||
|
use magnetar_sdk::endpoints::user::{GetUserById, GetUserSelf, UserByIdReq, UserSelfReq};
|
||||||
|
use magnetar_sdk::endpoints::{Req, Res};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
// TODO: Not a real endpoint
|
pub async fn handle_user_info_self(
|
||||||
pub async fn handle_user_info(
|
Query(UserSelfReq {
|
||||||
State(service): State<Arc<MagnetarService>>,
|
detail: _,
|
||||||
AuthenticatedUser(user): AuthenticatedUser,
|
pins: _,
|
||||||
) -> Result<impl IntoResponse, ApiError> {
|
profile: _,
|
||||||
let db = service.db.clone();
|
secrets: _,
|
||||||
let user = db.get_user_by_id(&user.id).await?;
|
}): Query<Req<GetUserSelf>>,
|
||||||
|
State(_service): State<Arc<MagnetarService>>,
|
||||||
Ok(Json(user))
|
AuthenticatedUser(_user): AuthenticatedUser,
|
||||||
|
) -> Result<Json<Res<GetUserSelf>>, ApiError> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handle_user_info(
|
||||||
|
Path(_id): Path<String>,
|
||||||
|
Query(UserByIdReq {
|
||||||
|
detail: _,
|
||||||
|
pins: _,
|
||||||
|
profile: _,
|
||||||
|
relation: _,
|
||||||
|
auth: _,
|
||||||
|
}): Query<Req<GetUserById>>,
|
||||||
|
State(_service): State<Arc<MagnetarService>>,
|
||||||
|
MaybeUser(_user): MaybeUser,
|
||||||
|
) -> Result<Json<Res<GetUserById>>, ApiError> {
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
mod api_v1;
|
mod api_v1;
|
||||||
|
pub mod model;
|
||||||
pub mod nodeinfo;
|
pub mod nodeinfo;
|
||||||
pub mod service;
|
pub mod service;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
use magnetar_calckey_model::ck;
|
||||||
|
use magnetar_sdk::types::drive::{DriveFileBase, ImageMeta};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::model::{PackType, PackingContext};
|
||||||
|
|
||||||
|
impl PackType<&ck::drive_file::Model> for DriveFileBase {
|
||||||
|
fn extract(_context: &PackingContext, file: &ck::drive_file::Model) -> Self {
|
||||||
|
let media_metadata = ImageMeta::deserialize(file.properties.clone()).unwrap_or_default();
|
||||||
|
|
||||||
|
DriveFileBase {
|
||||||
|
name: file.name.clone(),
|
||||||
|
created_at: file.created_at.into(),
|
||||||
|
size: file.size as u64,
|
||||||
|
hash: None, // TODO: blake3
|
||||||
|
mime_type: file.r#type.clone(),
|
||||||
|
media_metadata,
|
||||||
|
url: Some(file.url.clone()),
|
||||||
|
thumbnail_url: file.thumbnail_url.clone(),
|
||||||
|
sensitive: file.is_sensitive,
|
||||||
|
comment: file.comment.clone(),
|
||||||
|
folder_id: file.folder_id.clone(),
|
||||||
|
user_id: file.user_id.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
use crate::model::{PackType, PackingContext};
|
||||||
|
use magnetar_calckey_model::ck;
|
||||||
|
use magnetar_sdk::types::emoji::EmojiBase;
|
||||||
|
|
||||||
|
impl PackType<&ck::emoji::Model> for EmojiBase {
|
||||||
|
fn extract(_context: &PackingContext, emoji: &ck::emoji::Model) -> Self {
|
||||||
|
EmojiBase {
|
||||||
|
shortcode: emoji.name.clone(),
|
||||||
|
category: emoji.category.clone(),
|
||||||
|
url: emoji.public_url.clone(),
|
||||||
|
width: emoji.width,
|
||||||
|
height: emoji.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
use magnetar_calckey_model::ck;
|
||||||
|
|
||||||
|
pub trait BaseId: 'static {
|
||||||
|
fn get_id(&self) -> &str;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_id {
|
||||||
|
($id:ty) => {
|
||||||
|
impl BaseId for $id {
|
||||||
|
fn get_id(&self) -> &str {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_id!(ck::emoji::Model);
|
||||||
|
impl_id!(ck::user::Model);
|
||||||
|
impl_id!(ck::note::Model);
|
|
@ -0,0 +1,5 @@
|
||||||
|
pub mod drive;
|
||||||
|
pub mod emoji;
|
||||||
|
pub mod id;
|
||||||
|
pub mod note;
|
||||||
|
pub mod user;
|
|
@ -0,0 +1,83 @@
|
||||||
|
use magnetar_calckey_model::ck;
|
||||||
|
use magnetar_sdk::types::note::Reaction;
|
||||||
|
use magnetar_sdk::types::user::PackUserBase;
|
||||||
|
use magnetar_sdk::types::{
|
||||||
|
drive::PackDriveFileBase,
|
||||||
|
emoji::EmojiContext,
|
||||||
|
note::{NoteAttachmentExt, NoteBase, NoteVisibility, PackReactionBase, ReactionBase},
|
||||||
|
user::UserBase,
|
||||||
|
};
|
||||||
|
use magnetar_sdk::{Packed, Required};
|
||||||
|
|
||||||
|
use crate::model::{PackType, PackingContext};
|
||||||
|
|
||||||
|
impl PackType<&ck::note_reaction::Model> for ReactionBase {
|
||||||
|
fn extract(context: &PackingContext, reaction: &ck::note_reaction::Model) -> Self {
|
||||||
|
ReactionBase {
|
||||||
|
created_at: reaction.created_at.into(),
|
||||||
|
user_id: reaction.user_id.clone(),
|
||||||
|
reaction: Reaction::guess_from(
|
||||||
|
&reaction.reaction,
|
||||||
|
&context.instance_info.default_reaction,
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|| /* Shouldn't happen */ Reaction::Unicode("👍".to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NoteBaseSource<'a> {
|
||||||
|
pub note: &'a ck::note::Model,
|
||||||
|
pub reactions: &'a Vec<PackReactionBase>,
|
||||||
|
pub emojis: &'a EmojiContext,
|
||||||
|
pub user: &'a UserBase,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackType<NoteBaseSource<'_>> for NoteBase {
|
||||||
|
fn extract(
|
||||||
|
_context: &PackingContext,
|
||||||
|
NoteBaseSource {
|
||||||
|
note,
|
||||||
|
reactions,
|
||||||
|
emojis,
|
||||||
|
user,
|
||||||
|
}: NoteBaseSource<'_>,
|
||||||
|
) -> Self {
|
||||||
|
use ck::sea_orm_active_enums::NoteVisibilityEnum as NVE;
|
||||||
|
NoteBase {
|
||||||
|
created_at: note.created_at.into(),
|
||||||
|
cw: note.cw.clone(),
|
||||||
|
uri: note.uri.clone(),
|
||||||
|
url: note.url.clone(),
|
||||||
|
text: note.text.clone().unwrap_or_default(),
|
||||||
|
visibility: match note.visibility {
|
||||||
|
NVE::Followers => NoteVisibility::Followers,
|
||||||
|
NVE::Hidden => NoteVisibility::Direct,
|
||||||
|
NVE::Public => NoteVisibility::Public,
|
||||||
|
NVE::Home => NoteVisibility::Home,
|
||||||
|
NVE::Specified => NoteVisibility::Direct,
|
||||||
|
},
|
||||||
|
user: Box::new(PackUserBase::pack_from((
|
||||||
|
Required(note.user_id.as_str().into()),
|
||||||
|
Required(user.clone()),
|
||||||
|
))),
|
||||||
|
parent_note_id: note.reply_id.clone(),
|
||||||
|
renoted_note_id: note.renote_id.clone(),
|
||||||
|
reply_count: note.replies_count as u64,
|
||||||
|
renote_count: note.renote_count as u64,
|
||||||
|
hashtags: note.tags.clone(),
|
||||||
|
reactions: reactions.clone(),
|
||||||
|
emojis: emojis.clone(),
|
||||||
|
local_only: note.local_only,
|
||||||
|
has_poll: note.has_poll,
|
||||||
|
file_ids: note.file_ids.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackType<&Vec<PackDriveFileBase>> for NoteAttachmentExt {
|
||||||
|
fn extract(_context: &PackingContext, data: &Vec<PackDriveFileBase>) -> Self {
|
||||||
|
NoteAttachmentExt {
|
||||||
|
attachments: data.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,184 @@
|
||||||
|
use magnetar_calckey_model::ck;
|
||||||
|
use magnetar_calckey_model::ck::sea_orm_active_enums::UserProfileFfvisibilityEnum;
|
||||||
|
use magnetar_sdk::types::emoji::{EmojiContext, PackEmojiBase};
|
||||||
|
use magnetar_sdk::types::note::PackNoteFull;
|
||||||
|
use magnetar_sdk::types::user::{
|
||||||
|
AvatarDecoration, PackSecurityKeyBase, SecurityKeyBase, SpeechTransform, UserBase,
|
||||||
|
UserDetailExt, UserProfileExt, UserProfilePinsEx, UserRelationExt, UserSecretsExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::model::{PackType, PackingContext};
|
||||||
|
|
||||||
|
impl PackType<&[PackEmojiBase]> for EmojiContext {
|
||||||
|
fn extract(_context: &PackingContext, data: &[PackEmojiBase]) -> Self {
|
||||||
|
EmojiContext(data.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserBaseSource<'a> = (
|
||||||
|
&'a ck::user::Model,
|
||||||
|
&'a Option<ck::drive_file::Model>,
|
||||||
|
&'a EmojiContext,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl PackType<UserBaseSource<'_>> for UserBase {
|
||||||
|
fn extract(_context: &PackingContext, (user, avatar, emoji_context): UserBaseSource) -> Self {
|
||||||
|
UserBase {
|
||||||
|
acct: user
|
||||||
|
.host
|
||||||
|
.as_ref()
|
||||||
|
.map(|host| format!("@{}@{}", user.username, host))
|
||||||
|
.unwrap_or_else(|| format!("@{}", user.username)),
|
||||||
|
username: user.username.clone(),
|
||||||
|
display_name: user.name.clone().unwrap_or_else(|| user.username.clone()),
|
||||||
|
host: user.host.clone(),
|
||||||
|
speech_transform: if user.is_cat && user.speak_as_cat {
|
||||||
|
SpeechTransform::Cat
|
||||||
|
} else {
|
||||||
|
SpeechTransform::None
|
||||||
|
},
|
||||||
|
created_at: user.created_at.into(),
|
||||||
|
avatar_url: avatar.as_ref().map(|v| v.url.clone()),
|
||||||
|
avatar_blurhash: avatar.as_ref().and_then(|v| v.blurhash.clone()),
|
||||||
|
avatar_color: None,
|
||||||
|
avatar_decoration: if user.is_cat {
|
||||||
|
AvatarDecoration::CatEars
|
||||||
|
} else {
|
||||||
|
AvatarDecoration::None
|
||||||
|
},
|
||||||
|
is_admin: user.is_admin,
|
||||||
|
is_moderator: user.is_moderator,
|
||||||
|
is_bot: user.is_bot,
|
||||||
|
emojis: emoji_context.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserProfileExtSource<'a> = (
|
||||||
|
&'a ck::user::Model,
|
||||||
|
&'a ck::user_profile::Model,
|
||||||
|
Option<&'a UserRelationExt>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl PackType<UserProfileExtSource<'_>> for UserProfileExt {
|
||||||
|
fn extract(context: &PackingContext, (user, profile, relation): UserProfileExtSource) -> Self {
|
||||||
|
let follow_visibility = match profile.ff_visibility {
|
||||||
|
UserProfileFfvisibilityEnum::Public => true,
|
||||||
|
UserProfileFfvisibilityEnum::Followers => relation.is_some_and(|r| r.follows_you),
|
||||||
|
UserProfileFfvisibilityEnum::Private => false,
|
||||||
|
} || context.is_self(user);
|
||||||
|
|
||||||
|
UserProfileExt {
|
||||||
|
is_locked: user.is_locked,
|
||||||
|
is_silenced: user.is_silenced,
|
||||||
|
is_suspended: user.is_suspended,
|
||||||
|
description: profile.description.clone(),
|
||||||
|
location: profile.location.clone(),
|
||||||
|
birthday: profile
|
||||||
|
.birthday
|
||||||
|
.clone()
|
||||||
|
.and_then(|b| b.parse().map_or_else(|_| None, Some)),
|
||||||
|
fields: serde_json::from_value(profile.fields.clone()).unwrap_or_else(|_| Vec::new()),
|
||||||
|
follower_count: follow_visibility.then_some(user.followers_count as u64),
|
||||||
|
following_count: follow_visibility.then_some(user.following_count as u64),
|
||||||
|
note_count: Some(user.notes_count as u64),
|
||||||
|
url: profile.url.clone(),
|
||||||
|
moved_to_uri: user.moved_to_uri.clone(),
|
||||||
|
also_known_as: user.also_known_as.clone(),
|
||||||
|
banner_url: None,
|
||||||
|
banner_color: None,
|
||||||
|
banner_blurhash: None,
|
||||||
|
has_public_reactions: profile.public_reactions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackType<&ck::user::Model> for UserDetailExt {
|
||||||
|
fn extract(_context: &PackingContext, user: &ck::user::Model) -> Self {
|
||||||
|
UserDetailExt {
|
||||||
|
last_fetched_at: user.last_fetched_at.map(Into::into),
|
||||||
|
uri: user.uri.clone(),
|
||||||
|
updated_at: user.updated_at.map(Into::into),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserRelationExtSource<'a> = (
|
||||||
|
Option<&'a ck::following::Model>,
|
||||||
|
Option<&'a ck::following::Model>,
|
||||||
|
Option<&'a ck::blocking::Model>,
|
||||||
|
Option<&'a ck::blocking::Model>,
|
||||||
|
Option<&'a ck::muting::Model>,
|
||||||
|
Option<&'a ck::renote_muting::Model>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl PackType<UserRelationExtSource<'_>> for UserRelationExt {
|
||||||
|
fn extract(
|
||||||
|
context: &PackingContext,
|
||||||
|
(follow_out, follow_in, block_out, block_in, mute, renote_mute): UserRelationExtSource,
|
||||||
|
) -> Self {
|
||||||
|
let self_user = context.self_user();
|
||||||
|
|
||||||
|
UserRelationExt {
|
||||||
|
follows_you: self_user.is_some_and(|self_user| {
|
||||||
|
follow_in.is_some_and(|follow_in| {
|
||||||
|
self_user.id == follow_in.followee_id && self_user.id != follow_in.follower_id
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
you_follow: self_user.is_some_and(|self_user| {
|
||||||
|
follow_out.is_some_and(|follow_out| {
|
||||||
|
self_user.id == follow_out.follower_id && self_user.id != follow_out.followee_id
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
blocks_you: self_user.is_some_and(|self_user| {
|
||||||
|
block_in.is_some_and(|block_in| {
|
||||||
|
self_user.id == block_in.blockee_id && self_user.id != block_in.blocker_id
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
you_block: self_user.is_some_and(|self_user| {
|
||||||
|
block_out.is_some_and(|block_out| {
|
||||||
|
self_user.id == block_out.blocker_id && self_user.id != block_out.blockee_id
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
mute: self_user.is_some_and(|self_user| {
|
||||||
|
mute.is_some_and(|mute| {
|
||||||
|
self_user.id == mute.muter_id && self_user.id != mute.mutee_id
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
mute_renotes: self_user.is_some_and(|self_user| {
|
||||||
|
renote_mute.is_some_and(|renote_mute| {
|
||||||
|
self_user.id == renote_mute.muter_id && self_user.id != renote_mute.mutee_id
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackType<&[PackNoteFull]> for UserProfilePinsEx {
|
||||||
|
fn extract(_context: &PackingContext, pinned_notes: &[PackNoteFull]) -> Self {
|
||||||
|
UserProfilePinsEx {
|
||||||
|
pinned_notes: pinned_notes.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackType<ck::user_security_key::Model> for SecurityKeyBase {
|
||||||
|
fn extract(_context: &PackingContext, key: ck::user_security_key::Model) -> Self {
|
||||||
|
SecurityKeyBase {
|
||||||
|
name: key.name.clone(),
|
||||||
|
last_used_at: Some(key.last_used.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserSecretsExtSource<'a> = (&'a ck::user_profile::Model, &'a [PackSecurityKeyBase]);
|
||||||
|
|
||||||
|
impl PackType<UserSecretsExtSource<'_>> for UserSecretsExt {
|
||||||
|
fn extract(_context: &PackingContext, (profile, keys): UserSecretsExtSource) -> Self {
|
||||||
|
UserSecretsExt {
|
||||||
|
email: profile.email.clone(),
|
||||||
|
email_verified: profile.email_verified,
|
||||||
|
security_keys: keys.to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
use magnetar_calckey_model::ck;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub mod data;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PackingContext {
|
||||||
|
instance_info: Arc<ck::meta::Model>,
|
||||||
|
self_user: Option<Arc<ck::user::Model>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PackType<I>: 'static {
|
||||||
|
fn extract(context: &PackingContext, data: I) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PackingContext {
|
||||||
|
fn self_user(&self) -> Option<&ck::user::Model> {
|
||||||
|
self.self_user.as_deref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_self(&self, user: &ck::user::Model) -> bool {
|
||||||
|
self.self_user()
|
||||||
|
.is_some_and(|self_user| self_user.id == user.id)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::web::ApiError;
|
use crate::web::ApiError;
|
||||||
use cached::{Cached, TimedCache};
|
use cached::{Cached, TimedCache};
|
||||||
use magnetar_calckey_model::{
|
use magnetar_calckey_model::{
|
||||||
user, CalckeyCache, CalckeyCacheError, CalckeyDbError, CalckeyModel, CalckeySub,
|
ck, CalckeyCache, CalckeyCacheError, CalckeyDbError, CalckeyModel, CalckeySub,
|
||||||
InternalStreamMessage, SubMessage,
|
InternalStreamMessage, SubMessage,
|
||||||
};
|
};
|
||||||
use magnetar_common::config::MagnetarConfig;
|
use magnetar_common::config::MagnetarConfig;
|
||||||
|
@ -35,13 +35,13 @@ impl From<UserCacheError> for ApiError {
|
||||||
|
|
||||||
struct UserCache {
|
struct UserCache {
|
||||||
lifetime: TimedCache<String, ()>,
|
lifetime: TimedCache<String, ()>,
|
||||||
id_to_user: HashMap<String, Arc<user::Model>>,
|
id_to_user: HashMap<String, Arc<ck::user::Model>>,
|
||||||
token_to_user: HashMap<String, Arc<user::Model>>,
|
token_to_user: HashMap<String, Arc<ck::user::Model>>,
|
||||||
uri_to_user: HashMap<String, Arc<user::Model>>,
|
uri_to_user: HashMap<String, Arc<ck::user::Model>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserCache {
|
impl UserCache {
|
||||||
fn purge(&mut self, user: impl AsRef<user::Model>) {
|
fn purge(&mut self, user: impl AsRef<ck::user::Model>) {
|
||||||
let user = user.as_ref();
|
let user = user.as_ref();
|
||||||
|
|
||||||
self.lifetime.cache_remove(&user.id);
|
self.lifetime.cache_remove(&user.id);
|
||||||
|
@ -56,7 +56,7 @@ impl UserCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh(&mut self, user: Arc<user::Model>) {
|
fn refresh(&mut self, user: Arc<ck::user::Model>) {
|
||||||
self.purge(&user);
|
self.purge(&user);
|
||||||
|
|
||||||
self.lifetime.cache_set(user.id.clone(), ());
|
self.lifetime.cache_set(user.id.clone(), ());
|
||||||
|
@ -74,13 +74,13 @@ impl UserCache {
|
||||||
|
|
||||||
/// Low-priority refresh. Only refreshes the cache if the user is not there.
|
/// Low-priority refresh. Only refreshes the cache if the user is not there.
|
||||||
/// Used mostly for getters that would otherwise data race with more important refreshes.
|
/// Used mostly for getters that would otherwise data race with more important refreshes.
|
||||||
fn maybe_refresh(&mut self, user: &Arc<user::Model>) {
|
fn maybe_refresh(&mut self, user: &Arc<ck::user::Model>) {
|
||||||
if self.lifetime.cache_get(&user.id).is_none() {
|
if self.lifetime.cache_get(&user.id).is_none() {
|
||||||
self.refresh(user.clone());
|
self.refresh(user.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_by_id(&mut self, id: &str) -> Option<Arc<user::Model>> {
|
fn get_by_id(&mut self, id: &str) -> Option<Arc<ck::user::Model>> {
|
||||||
if let Some(user) = self.id_to_user.get(id).cloned() {
|
if let Some(user) = self.id_to_user.get(id).cloned() {
|
||||||
if self.lifetime.cache_get(id).is_none() {
|
if self.lifetime.cache_get(id).is_none() {
|
||||||
self.purge(&user);
|
self.purge(&user);
|
||||||
|
@ -93,7 +93,7 @@ impl UserCache {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_by_token(&mut self, token: &str) -> Option<Arc<user::Model>> {
|
fn get_by_token(&mut self, token: &str) -> Option<Arc<ck::user::Model>> {
|
||||||
if let Some(user) = self.token_to_user.get(token).cloned() {
|
if let Some(user) = self.token_to_user.get(token).cloned() {
|
||||||
if self.lifetime.cache_get(&user.id).is_none() {
|
if self.lifetime.cache_get(&user.id).is_none() {
|
||||||
self.purge(&user);
|
self.purge(&user);
|
||||||
|
@ -106,7 +106,7 @@ impl UserCache {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_by_uri(&mut self, uri: &str) -> Option<Arc<user::Model>> {
|
fn get_by_uri(&mut self, uri: &str) -> Option<Arc<ck::user::Model>> {
|
||||||
if let Some(user) = self.uri_to_user.get(uri).cloned() {
|
if let Some(user) = self.uri_to_user.get(uri).cloned() {
|
||||||
if self.lifetime.cache_get(&user.id).is_none() {
|
if self.lifetime.cache_get(&user.id).is_none() {
|
||||||
self.purge(&user);
|
self.purge(&user);
|
||||||
|
@ -188,8 +188,8 @@ impl UserCacheService {
|
||||||
|
|
||||||
async fn map_cache_user(
|
async fn map_cache_user(
|
||||||
&self,
|
&self,
|
||||||
user: Option<user::Model>,
|
user: Option<ck::user::Model>,
|
||||||
) -> Result<Option<Arc<user::Model>>, UserCacheError> {
|
) -> Result<Option<Arc<ck::user::Model>>, UserCacheError> {
|
||||||
if let Some(user) = user {
|
if let Some(user) = user {
|
||||||
let user = Arc::new(user);
|
let user = Arc::new(user);
|
||||||
self.cache.lock().await.maybe_refresh(&user);
|
self.cache.lock().await.maybe_refresh(&user);
|
||||||
|
@ -202,7 +202,7 @@ impl UserCacheService {
|
||||||
pub async fn get_by_token(
|
pub async fn get_by_token(
|
||||||
&self,
|
&self,
|
||||||
token: &str,
|
token: &str,
|
||||||
) -> Result<Option<Arc<user::Model>>, UserCacheError> {
|
) -> Result<Option<Arc<ck::user::Model>>, UserCacheError> {
|
||||||
let result = self.cache.lock().await.get_by_token(token);
|
let result = self.cache.lock().await.get_by_token(token);
|
||||||
|
|
||||||
if let Some(user) = result {
|
if let Some(user) = result {
|
||||||
|
@ -213,7 +213,10 @@ impl UserCacheService {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_by_uri(&self, uri: &str) -> Result<Option<Arc<user::Model>>, UserCacheError> {
|
pub async fn get_by_uri(
|
||||||
|
&self,
|
||||||
|
uri: &str,
|
||||||
|
) -> Result<Option<Arc<ck::user::Model>>, UserCacheError> {
|
||||||
let result = self.cache.lock().await.get_by_uri(uri);
|
let result = self.cache.lock().await.get_by_uri(uri);
|
||||||
|
|
||||||
if let Some(user) = result {
|
if let Some(user) = result {
|
||||||
|
@ -225,7 +228,10 @@ impl UserCacheService {
|
||||||
self.map_cache_user(user).await
|
self.map_cache_user(user).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_by_id(&self, id: &str) -> Result<Option<Arc<user::Model>>, UserCacheError> {
|
pub async fn get_by_id(
|
||||||
|
&self,
|
||||||
|
id: &str,
|
||||||
|
) -> Result<Option<Arc<ck::user::Model>>, UserCacheError> {
|
||||||
let result = self.cache.lock().await.get_by_id(id);
|
let result = self.cache.lock().await.get_by_id(id);
|
||||||
|
|
||||||
if let Some(user) = result {
|
if let Some(user) = result {
|
||||||
|
|
|
@ -10,7 +10,7 @@ use axum::middleware::Next;
|
||||||
use axum::response::{IntoResponse, Response};
|
use axum::response::{IntoResponse, Response};
|
||||||
use headers::authorization::Bearer;
|
use headers::authorization::Bearer;
|
||||||
use headers::{Authorization, HeaderMapExt};
|
use headers::{Authorization, HeaderMapExt};
|
||||||
use magnetar_calckey_model::{access_token, user, CalckeyDbError};
|
use magnetar_calckey_model::{ck, CalckeyDbError};
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use strum::IntoStaticStr;
|
use strum::IntoStaticStr;
|
||||||
|
@ -20,17 +20,17 @@ use tracing::error;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AuthMode {
|
pub enum AuthMode {
|
||||||
User {
|
User {
|
||||||
user: Arc<user::Model>,
|
user: Arc<ck::user::Model>,
|
||||||
},
|
},
|
||||||
AccessToken {
|
AccessToken {
|
||||||
user: Arc<user::Model>,
|
user: Arc<ck::user::Model>,
|
||||||
access_token: Arc<access_token::Model>,
|
access_token: Arc<ck::access_token::Model>,
|
||||||
},
|
},
|
||||||
Anonymous,
|
Anonymous,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AuthMode {
|
impl AuthMode {
|
||||||
fn get_user(&self) -> Option<&Arc<user::Model>> {
|
fn get_user(&self) -> Option<&Arc<ck::user::Model>> {
|
||||||
match self {
|
match self {
|
||||||
AuthMode::User { user } | AuthMode::AccessToken { user, .. } => Some(user),
|
AuthMode::User { user } | AuthMode::AccessToken { user, .. } => Some(user),
|
||||||
AuthMode::Anonymous => None,
|
AuthMode::Anonymous => None,
|
||||||
|
@ -62,10 +62,10 @@ impl IntoResponse for AuthUserRejection {
|
||||||
|
|
||||||
#[derive(Clone, FromRequestParts)]
|
#[derive(Clone, FromRequestParts)]
|
||||||
#[from_request(via(axum::Extension), rejection(AuthUserRejection))]
|
#[from_request(via(axum::Extension), rejection(AuthUserRejection))]
|
||||||
pub struct AuthenticatedUser(pub Arc<user::Model>);
|
pub struct AuthenticatedUser(pub Arc<ck::user::Model>);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MaybeUser(pub Option<Arc<user::Model>>);
|
pub struct MaybeUser(pub Option<Arc<ck::user::Model>>);
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<S> FromRequestParts<S> for MaybeUser {
|
impl<S> FromRequestParts<S> for MaybeUser {
|
||||||
|
@ -211,7 +211,7 @@ impl AuthState {
|
||||||
return match self.service.db.get_app_by_id(app_id).await? {
|
return match self.service.db.get_app_by_id(app_id).await? {
|
||||||
Some(app) => Ok(AuthMode::AccessToken {
|
Some(app) => Ok(AuthMode::AccessToken {
|
||||||
user,
|
user,
|
||||||
access_token: Arc::new(access_token::Model {
|
access_token: Arc::new(ck::access_token::Model {
|
||||||
permission: app.permission,
|
permission: app.permission,
|
||||||
..access_token
|
..access_token
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -21,7 +21,9 @@ pub struct WebFingerQuery {
|
||||||
|
|
||||||
// TODO: Filter by rel
|
// TODO: Filter by rel
|
||||||
pub async fn handle_webfinger(
|
pub async fn handle_webfinger(
|
||||||
Query(WebFingerQuery { resource, rel, .. }): Query<WebFingerQuery>,
|
Query(WebFingerQuery {
|
||||||
|
resource, rel: _, ..
|
||||||
|
}): Query<WebFingerQuery>,
|
||||||
State((config, ck)): State<(&'static MagnetarConfig, CalckeyModel)>,
|
State((config, ck)): State<(&'static MagnetarConfig, CalckeyModel)>,
|
||||||
) -> Result<impl IntoResponse, StatusCode> {
|
) -> Result<impl IntoResponse, StatusCode> {
|
||||||
let resource = match resource {
|
let resource = match resource {
|
||||||
|
|
Loading…
Reference in New Issue