Compare commits

...

9 Commits

Author SHA1 Message Date
Amatsugu 8ff4fa74e4 wip passkey registration day 2 2026-04-12 16:59:40 -04:00
Amatsugu 6c2305cac9 wip passkey registration 2026-04-11 19:02:06 -04:00
Amatsugu 7a43d5c11f wip passkey registration 2026-04-11 18:56:04 -04:00
Amatsugu 4325280020 context menu implementation testing 2026-04-10 14:34:24 -04:00
Amatsugu ec0c6a3487 push history state
Build and Push Image / build-and-push (push) Successful in 5m12s
2026-04-10 01:15:51 -04:00
Amatsugu dd8faf8038 update 2026-04-08 22:45:29 -04:00
Amatsugu bd99b4beac update dx 2026-04-05 19:39:00 -04:00
Amatsugu 2517cd777f misc passkey prep 2026-04-05 19:20:02 -04:00
Amatsugu ea9ad2f8a7 rewrite components such that data always flows downwards 2026-04-05 17:03:19 -04:00
28 changed files with 588 additions and 338 deletions
+130 -93
View File
@@ -24,8 +24,10 @@ dependencies = [
"dioxus", "dioxus",
"dioxus-primitives", "dioxus-primitives",
"dotenv", "dotenv",
"js-sys",
"prost", "prost",
"serde", "serde",
"serde-wasm-bindgen",
"serde_repr", "serde_repr",
"tonic", "tonic",
"tonic-build", "tonic-build",
@@ -79,7 +81,7 @@ dependencies = [
"futures-util", "futures-util",
"log", "log",
"pin-project-lite", "pin-project-lite",
"tungstenite", "tungstenite 0.27.0",
] ]
[[package]] [[package]]
@@ -509,9 +511,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus" name = "dioxus"
version = "0.7.3" version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92b583b48ac77158495e6678fe3a2b5954fc8866fc04cb9695dd146e88bc329d" checksum = "a44c550c06b6785e16258ad620d5b559f5bbcbcc50e3c18c08aa6af2604a4c32"
dependencies = [ dependencies = [
"dioxus-asset-resolver", "dioxus-asset-resolver",
"dioxus-cli-config", "dioxus-cli-config",
@@ -537,9 +539,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-asset-resolver" name = "dioxus-asset-resolver"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0161af1d3cfc8ff31503ff1b7ee0068c97771fc38d0cc6566e23483142ddf4f" checksum = "c240c4f092024b26e200ecd64723009173cf5bc2e5083c9feb778c077eb5741b"
dependencies = [ dependencies = [
"dioxus-cli-config", "dioxus-cli-config",
"http", "http",
@@ -569,18 +571,18 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-cli-config" name = "dioxus-cli-config"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccd67ab405e1915a47df9769cd5408545d1b559d5c01ce7a0f442caef520d1f3" checksum = "86a13d42c5defcea333bdbae1dc5d64d078acd0fda1d8a1441c37e06be5146e3"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]] [[package]]
name = "dioxus-config-macro" name = "dioxus-config-macro"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f040ec7c41aa5428283f56bb0670afba9631bfe3ffd885f4814807f12c8c9d91" checksum = "1ba1d68a05a8a15293ba65d45c7a3263356f3eedf1a3e599440683f3eb014637"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -588,15 +590,15 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-config-macros" name = "dioxus-config-macros"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10c41b47b55a433b61f7c12327c85ba650572bacbcc42c342ba2e87a57975264" checksum = "f43f2d511d3c3c439a2fb7f863668b84caf8e0d2440cbfbcbb28521e26ba7f44"
[[package]] [[package]]
name = "dioxus-core" name = "dioxus-core"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b389b0e3cc01c7da292ad9b884b088835fdd1671d45fbd2f737506152b22eef0" checksum = "fb3dd61889e6a09daec93d44db86047fb8e6603beedcf9351b8528582254e075"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"const_format", "const_format",
@@ -616,9 +618,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-core-macro" name = "dioxus-core-macro"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82d65f0024fc86f01911a16156d280eea583be5a82a3bed85e7e8e4194302d" checksum = "8577c4d9a8cc23423c4d2137319044b03ab940e4b2790dd25f4f06601bd32d9a"
dependencies = [ dependencies = [
"convert_case 0.8.0", "convert_case 0.8.0",
"dioxus-rsx", "dioxus-rsx",
@@ -629,15 +631,15 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-core-types" name = "dioxus-core-types"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfc4b8cdc440a55c17355542fc2089d97949bba674255d84cac77805e1db8c9f" checksum = "b99d7d199aad72431b549759550002e7d72c8a257eba500dca9fbdb2122de103"
[[package]] [[package]]
name = "dioxus-devtools" name = "dioxus-devtools"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf89488bad8fb0f18b9086ee2db01f95f709801c10c68be42691a36378a0f2d" checksum = "d27e7212436a581ce058d7554f1383916bd18a68ebd6015b0b4c2e9ecb0d5535"
dependencies = [ dependencies = [
"dioxus-cli-config", "dioxus-cli-config",
"dioxus-core", "dioxus-core",
@@ -648,14 +650,14 @@ dependencies = [
"subsecond", "subsecond",
"thiserror 2.0.18", "thiserror 2.0.18",
"tracing", "tracing",
"tungstenite", "tungstenite 0.28.0",
] ]
[[package]] [[package]]
name = "dioxus-devtools-types" name = "dioxus-devtools-types"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e7381d9d7d0a0f66b9d5082d584853c3d53be21d34007073daca98ddf26fc4d" checksum = "6aa24ed651b97e0b423270bf07a0f1b7dc0e0fa1f1dc26407cd2a118d6bf9de5"
dependencies = [ dependencies = [
"dioxus-core", "dioxus-core",
"serde", "serde",
@@ -664,9 +666,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-document" name = "dioxus-document"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba0aeeff26d9d06441f59fd8d7f4f76098ba30ca9728e047c94486161185ceb" checksum = "24685cb51cc6227ea606c49dfe531836f362c49183d3007241afcd8827498401"
dependencies = [ dependencies = [
"dioxus-core", "dioxus-core",
"dioxus-core-macro", "dioxus-core-macro",
@@ -675,7 +677,7 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"generational-box", "generational-box",
"lazy-js-bundle 0.7.3", "lazy-js-bundle 0.7.4",
"serde", "serde",
"serde_json", "serde_json",
"tracing", "tracing",
@@ -683,9 +685,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-fullstack" name = "dioxus-fullstack"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7db1f8b70338072ec408b48d09c96559cf071f87847465d8161294197504c498" checksum = "5940c870751b6273a23b7c0e16d80039f45604d68d9b86c91e27b09edeabeb9e"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-stream", "async-stream",
@@ -729,7 +731,7 @@ dependencies = [
"thiserror 2.0.18", "thiserror 2.0.18",
"tokio-util", "tokio-util",
"tracing", "tracing",
"tungstenite", "tungstenite 0.27.0",
"url", "url",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
@@ -740,9 +742,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-fullstack-core" name = "dioxus-fullstack-core"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda8b152e85121243741b9d5f2a3d8cb3c47a7b2299e902f98b6a7719915b0a2" checksum = "28333274cfc8e5fe547ab04258c2511350c4930a07af9616d365dc4ba7b22d8f"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"axum-core", "axum-core",
@@ -768,9 +770,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-fullstack-macro" name = "dioxus-fullstack-macro"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "255104d4a4f278f1a8482fa30536c91d22260c561c954b753e72987df8d65b2e" checksum = "53f7e5a9fa7f657aa519a07aced8b8936f3ae8a246d94855d497d8cce59b9533"
dependencies = [ dependencies = [
"const_format", "const_format",
"convert_case 0.8.0", "convert_case 0.8.0",
@@ -782,9 +784,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-history" name = "dioxus-history"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d00ba43bfe6e5ca226fef6128f240ca970bea73cac0462416188026360ccdcf" checksum = "010b446322b3f9176476579fa61c7552f0430abbeec418cab543482da6ca4363"
dependencies = [ dependencies = [
"dioxus-core", "dioxus-core",
"tracing", "tracing",
@@ -792,9 +794,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-hooks" name = "dioxus-hooks"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dab2da4f038c33cb38caa37ffc3f5d6dfbc018f05da35b238210a533bb075823" checksum = "09e7a6ba279050cc161e1215c6db0bd15915c9314ec2916d7b22c113a3039536"
dependencies = [ dependencies = [
"dioxus-core", "dioxus-core",
"dioxus-signals", "dioxus-signals",
@@ -808,9 +810,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-html" name = "dioxus-html"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eded5fa6d2e677b7442a93f4228bf3c0ad2597a8bd3292cae50c869d015f3a99" checksum = "f0715e38cc6537aef5b79d0ddc1f4d7a56c2f4debe46b127eee24d8aa5dafd2d"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"bytes", "bytes",
@@ -825,16 +827,16 @@ dependencies = [
"futures-util", "futures-util",
"generational-box", "generational-box",
"keyboard-types", "keyboard-types",
"lazy-js-bundle 0.7.3", "lazy-js-bundle 0.7.4",
"rustversion", "rustversion",
"tracing", "tracing",
] ]
[[package]] [[package]]
name = "dioxus-html-internal-macro" name = "dioxus-html-internal-macro"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45462ab85fe059a36841508d40545109fd0e25855012d22583a61908eb5cd02a" checksum = "ff6b7918b0908c8719a6165b4e3c362da4fd311fc7cb48720eddd8a45b2ddfc6"
dependencies = [ dependencies = [
"convert_case 0.8.0", "convert_case 0.8.0",
"proc-macro2", "proc-macro2",
@@ -844,12 +846,12 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-interpreter-js" name = "dioxus-interpreter-js"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a42a7f73ad32a5054bd8c1014f4ac78cca3b7f6889210ee2b57ea31b33b6d32f" checksum = "a8ce1cf487007f90d0ec4ec87dff111d74ac04fca0918f9dcc4e80dc3b0531b2"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"lazy-js-bundle 0.7.3", "lazy-js-bundle 0.7.4",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
"sledgehammer_bindgen", "sledgehammer_bindgen",
"sledgehammer_utils", "sledgehammer_utils",
@@ -860,9 +862,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-logger" name = "dioxus-logger"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1eeab114cb009d9e6b85ea10639a18cfc54bb342f3b837770b004c4daeb89c2" checksum = "d4742b16791a71eb4db2d0747f15c50b278b27369b3d93e5a4d6ec2570bcb9bc"
dependencies = [ dependencies = [
"dioxus-cli-config", "dioxus-cli-config",
"tracing", "tracing",
@@ -887,9 +889,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-router" name = "dioxus-router"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d5b31f9e27231389bf5a117b7074d22d8c58358b484a2558e56fbab20e64ca4" checksum = "ae50f5efa8d6f936c0c3bb85d7a55f6f19290f106290e331d1136d964e832fe6"
dependencies = [ dependencies = [
"dioxus-cli-config", "dioxus-cli-config",
"dioxus-core", "dioxus-core",
@@ -907,9 +909,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-router-macro" name = "dioxus-router-macro"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "838b9b441a95da62b39cae4defd240b5ebb0ec9f2daea1126099e00a838dc86f" checksum = "ae9beca02f6baca4b223256805536dc92e77a1541bb2331723100f66aae79332"
dependencies = [ dependencies = [
"base16", "base16",
"digest", "digest",
@@ -922,9 +924,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-rsx" name = "dioxus-rsx"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53128858f0ccca9de54292a4d48409fda1df75fd5012c6243f664042f0225d68" checksum = "344621f6dc435e76fbe272da09988d0118cf35cc2aa88ebb5ae7c1317a36e57c"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"proc-macro2-diagnostics", "proc-macro2-diagnostics",
@@ -947,9 +949,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-signals" name = "dioxus-signals"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f48020bc23bc9766e7cce986c0fd6de9af0b8cbfd432652ec6b1094439c1ec6" checksum = "409bf65d243443416650945f22cd6caf2a6bb13ae0347a50ec5852adb1961072"
dependencies = [ dependencies = [
"dioxus-core", "dioxus-core",
"futures-channel", "futures-channel",
@@ -963,9 +965,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-stores" name = "dioxus-stores"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77aaa9ac56d781bb506cf3c0d23bea96b768064b89fe50d3b4d4659cc6bd8058" checksum = "245ec4f84348e5be77451bd204181998b8bc0995b48ff3adb2db0e0ec430dab4"
dependencies = [ dependencies = [
"dioxus-core", "dioxus-core",
"dioxus-signals", "dioxus-signals",
@@ -975,9 +977,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-stores-macro" name = "dioxus-stores-macro"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b1a728622e7b63db45774f75e71504335dd4e6115b235bbcff272980499493a" checksum = "dd9da8e9a1cc2d8bff387e0b99f09f2590b71f67d5d73ab343b2cc9d17990d92"
dependencies = [ dependencies = [
"convert_case 0.8.0", "convert_case 0.8.0",
"proc-macro2", "proc-macro2",
@@ -987,9 +989,9 @@ dependencies = [
[[package]] [[package]]
name = "dioxus-web" name = "dioxus-web"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b33fe739fed4e8143dac222a9153593f8e2451662ce8fc4c9d167a9d6ec0923" checksum = "eac92ef863bc5333440021e8ec3e538a39598c9c960daeaab66ab10ba940b5e0"
dependencies = [ dependencies = [
"dioxus-cli-config", "dioxus-cli-config",
"dioxus-core", "dioxus-core",
@@ -1005,7 +1007,7 @@ dependencies = [
"generational-box", "generational-box",
"gloo-timers", "gloo-timers",
"js-sys", "js-sys",
"lazy-js-bundle 0.7.3", "lazy-js-bundle 0.7.4",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
"send_wrapper", "send_wrapper",
"serde", "serde",
@@ -1234,9 +1236,9 @@ dependencies = [
[[package]] [[package]]
name = "generational-box" name = "generational-box"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc4ed190b9de8e734d47a70be59b1e7588b9e8e0d0036e332f4c014e8aed1bc5" checksum = "4ede46ff252793f9b6ef752c506ba8600c69d73cad2ef9bbf2e6dee85019a3bc"
dependencies = [ dependencies = [
"parking_lot", "parking_lot",
"tracing", "tracing",
@@ -1669,10 +1671,12 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.91" version = "0.3.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca"
dependencies = [ dependencies = [
"cfg-if",
"futures-util",
"once_cell", "once_cell",
"wasm-bindgen", "wasm-bindgen",
] ]
@@ -1694,9 +1698,9 @@ checksum = "e49596223b9d9d4947a14a25c142a6e7d8ab3f27eb3ade269d238bb8b5c267e2"
[[package]] [[package]]
name = "lazy-js-bundle" name = "lazy-js-bundle"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7b88b715ab1496c6e6b8f5e927be961c4235196121b6ae59bcb51077a21dd36" checksum = "60d7adc10cb9440d17fa67e467febdfc98931338773d11bfee81809af54d0697"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
@@ -1778,21 +1782,25 @@ dependencies = [
[[package]] [[package]]
name = "manganis" name = "manganis"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cce7d688848bf9d034168513b9a2ffbfe5f61df2ff14ae15e6cfc866efdd344" checksum = "492da8d77990281eabe6ded633e7b0cf805c5cf7a023a99abed8811edc872d6f"
dependencies = [ dependencies = [
"const-serialize 0.7.2", "const-serialize 0.7.2",
"const-serialize 0.8.0-alpha.0", "const-serialize 0.8.0-alpha.0",
"jni",
"manganis-core", "manganis-core",
"manganis-macro", "manganis-macro",
"ndk-context",
"objc2",
"thiserror 2.0.18",
] ]
[[package]] [[package]]
name = "manganis-core" name = "manganis-core"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84ce917b978268fe8a7db49e216343ec7c8f471f7e686feb70940d67293f19d4" checksum = "a1b84cc2951f3b119702fab499b9b1aec3f454929c62feca55b895b82c628308"
dependencies = [ dependencies = [
"const-serialize 0.7.2", "const-serialize 0.7.2",
"const-serialize 0.8.0-alpha.0", "const-serialize 0.8.0-alpha.0",
@@ -1804,9 +1812,9 @@ dependencies = [
[[package]] [[package]]
name = "manganis-macro" name = "manganis-macro"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad513e990f7c0bca86aa68659a7a3dc4c705572ed4c22fd6af32ccf261334cc2" checksum = "6d2e60d36758b201b6ebb8a31aff6b013e58924eeb6d3cbf19aea764f51d69e4"
dependencies = [ dependencies = [
"dunce", "dunce",
"macro-string", "macro-string",
@@ -1990,6 +1998,21 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "objc2"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
dependencies = [
"objc2-encode",
]
[[package]]
name = "objc2-encode"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.3" version = "1.21.3"
@@ -2722,9 +2745,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]] [[package]]
name = "subsecond" name = "subsecond"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8438668e545834d795d04c4335aafc332ce046106521a29f0a5c6501de34187c" checksum = "5dbb9f2928b6654ccc28d4ddfef5213e97ed66afed4907774d049b376c62a838"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"libc", "libc",
@@ -2741,9 +2764,9 @@ dependencies = [
[[package]] [[package]]
name = "subsecond-types" name = "subsecond-types"
version = "0.7.3" version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e72f747606fc19fe81d6c59e491af93ed7dcbcb6aad9d1d18b05129914ec298" checksum = "388bb28e6ddbee717745963b8932d9a6e24a5d3c93350655f733e938de04d81f"
dependencies = [ dependencies = [
"serde", "serde",
] ]
@@ -2950,6 +2973,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-io", "futures-io",
"futures-sink", "futures-sink",
"futures-util",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
] ]
@@ -3169,6 +3193,23 @@ dependencies = [
"utf-8", "utf-8",
] ]
[[package]]
name = "tungstenite"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442"
dependencies = [
"bytes",
"data-encoding",
"http",
"httparse",
"log",
"rand",
"sha1",
"thiserror 2.0.18",
"utf-8",
]
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.19.0" version = "1.19.0"
@@ -3303,9 +3344,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.114" version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
@@ -3316,23 +3357,19 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-futures" name = "wasm-bindgen-futures"
version = "0.4.64" version = "0.4.68"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8"
dependencies = [ dependencies = [
"cfg-if",
"futures-util",
"js-sys", "js-sys",
"once_cell",
"wasm-bindgen", "wasm-bindgen",
"web-sys",
] ]
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.114" version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@@ -3340,9 +3377,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.114" version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"proc-macro2", "proc-macro2",
@@ -3353,9 +3390,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.114" version = "0.2.118"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@@ -3375,9 +3412,9 @@ dependencies = [
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.91" version = "0.3.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
+5 -3
View File
@@ -6,8 +6,8 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
dioxus = { version = "0.7.3", features = ["router"] } dioxus = { version = "0.7.5", features = ["router"] }
serde = "1.0.228" serde = { version = "1.0.228", features = ["derive"] }
serde_repr = "0.1.20" serde_repr = "0.1.20"
tonic = { version = "*", default-features = false, features = [ tonic = { version = "*", default-features = false, features = [
"codegen", "codegen",
@@ -15,8 +15,10 @@ tonic = { version = "*", default-features = false, features = [
] } ] }
prost = "0.13" prost = "0.13"
tonic-web-wasm-client = "0.7" tonic-web-wasm-client = "0.7"
web-sys = { version = "0.3.91", features = ["Storage", "Window"] } web-sys = { version = "0.3.91", features = ["Storage", "Window", "Navigator", "CredentialsContainer", "CredentialCreationOptions", "AttestationConveyancePreference", "PublicKeyCredentialCreationOptions", "PublicKeyCredentialRpEntity", "PublicKeyCredentialUserEntity"] }
dioxus-primitives = { git = "https://github.com/DioxusLabs/components", version = "0.0.1", default-features = false } dioxus-primitives = { git = "https://github.com/DioxusLabs/components", version = "0.0.1", default-features = false }
serde-wasm-bindgen = "0.6.5"
js-sys = "0.3.95"
[build-dependencies] [build-dependencies]
tonic-build = { version = "*", default-features = false, features = ["prost"] } tonic-build = { version = "*", default-features = false, features = ["prost"] }
+9 -4
View File
@@ -3,13 +3,15 @@ use std::env;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>>
{
tonic_build::configure() tonic_build::configure()
.build_server(false) .build_server(false)
.build_client(true) .build_client(true)
.compile_protos( .compile_protos(
&[ &[
"../AobaServer/Proto/Aoba.proto", "../AobaServer/Proto/Aoba.proto",
"../AobaServer/Proto/Account.proto",
"../AobaServer/Proto/Auth.proto", "../AobaServer/Proto/Auth.proto",
"../AobaServer/Proto/Metrics.proto", "../AobaServer/Proto/Metrics.proto",
"../AobaServer/Proto/Types.proto", "../AobaServer/Proto/Types.proto",
@@ -20,15 +22,18 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
Ok(()) Ok(())
} }
fn forward_env() { fn forward_env()
{
let dest_path = "./src/env.rs"; let dest_path = "./src/env.rs";
let mut f = File::create(&dest_path).unwrap(); let mut f = File::create(&dest_path).unwrap();
f.write_all(b"// This file is automatically generated by build.rs\n\n") f.write_all(b"// This file is automatically generated by build.rs\n\n")
.unwrap(); .unwrap();
dotenv().ok(); dotenv().ok();
for (key, value) in env::vars() { for (key, value) in env::vars()
if key.starts_with("APP_") { {
if key.starts_with("APP_")
{
f.write_all("#[allow(dead_code)]\n".as_bytes()).unwrap(); f.write_all("#[allow(dead_code)]\n".as_bytes()).unwrap();
let line = format!( let line = format!(
"pub const {}: &'static str = \"{}\";\n", "pub const {}: &'static str = \"{}\";\n",
@@ -1,64 +1,63 @@
use super::props::*;
use dioxus::prelude::*; use dioxus::prelude::*;
use dioxus_primitives::context_menu::{
self, ContextMenuContentProps, ContextMenuItemProps, ContextMenuProps, ContextMenuTriggerProps, const CONTEXT_MENU_CSS: Asset = asset!("./style.scss");
};
#[component] #[component]
pub fn ContextMenu(props: ContextMenuProps) -> Element { pub fn ContextMenu(props: ContextMenuProps) -> Element
rsx! { {
document::Link { rel: "stylesheet", href: asset!("./style.css") } rsx! {
context_menu::ContextMenu { document::Link { rel: "stylesheet", href: CONTEXT_MENU_CSS }
disabled: props.disabled, {props.children}
open: props.open, }
default_open: props.default_open,
on_open_change: props.on_open_change,
roving_loop: props.roving_loop,
attributes: props.attributes,
{props.children}
}
}
} }
#[component] #[component]
pub fn ContextMenuTrigger(props: ContextMenuTriggerProps) -> Element { pub fn ContextMenuTrigger(props: ContextMenuTriggerProps) -> Element
rsx! { {
context_menu::ContextMenuTrigger { rsx! {
padding: "20px", div{
background: "var(--primary-color)", class: "contextMenuTrigger",
border: "1px dashed var(--primary-color-6)", oncontextmenu: move|e|{
border_radius: ".5rem",
cursor: "context-menu", },
user_select: "none", {props.children}
text_align: "center", }
attributes: props.attributes, }
{props.children}
}
}
} }
#[component] #[component]
pub fn ContextMenuContent(props: ContextMenuContentProps) -> Element { pub fn ContextMenuContent(props: ContextMenuContentProps) -> Element
rsx! { {
context_menu::ContextMenuContent { rsx! {
class: "context-menu-content", div{
id: props.id, class: "contextMenuContent",
attributes: props.attributes, {props.children}
{props.children} }
} }
}
} }
#[component] #[component]
pub fn ContextMenuItem(props: ContextMenuItemProps) -> Element { pub fn ContextMenuItem(props: ContextMenuItemProps) -> Element
rsx! { {
context_menu::ContextMenuItem { rsx! {
class: "context-menu-item", div {
disabled: props.disabled, class: "contextMenuItem",
value: props.value, onclick: move |_|{
index: props.index, props.on_select.call(props.value.clone());
on_select: props.on_select, },
attributes: props.attributes, div {
{props.children} class: "content",
} {props.children}
} }
}
}
}
#[component]
pub fn ContextMenuNestedContent(props: ContextMenuNestedProps) -> Element
{
rsx! {
{props.children}
}
} }
@@ -1,2 +1,3 @@
mod component; mod component;
mod props;
pub use component::*; pub use component::*;
@@ -0,0 +1,33 @@
use dioxus::prelude::*;
#[derive(Props, Clone, PartialEq)]
pub struct ContextMenuProps
{
pub children: Element,
}
#[derive(Props, Clone, PartialEq)]
pub struct ContextMenuItemProps
{
pub value: String,
pub on_select: EventHandler<String>,
pub children: Element,
}
#[derive(Props, Clone, PartialEq)]
pub struct ContextMenuTriggerProps
{
pub children: Element,
}
#[derive(Props, Clone, PartialEq)]
pub struct ContextMenuContentProps
{
pub children: Element,
}
#[derive(Props, Clone, PartialEq)]
pub struct ContextMenuNestedProps
{
pub children: Element,
}
@@ -1,71 +0,0 @@
.context-menu-content {
z-index: 1000;
min-width: 220px;
padding: 0.25rem;
border-radius: 0.5rem;
background: var(--dark, var(--primary-color-5))
var(--light, var(--primary-color));
box-shadow: inset 0 0 0 1px var(--dark, var(--primary-color-7))
var(--light, var(--primary-color-6));
opacity: 0;
pointer-events: none;
will-change: transform, opacity;
}
.context-menu-content[data-state="closed"] {
animation: context-menu-animate-out 150ms ease-in forwards;
}
@keyframes context-menu-animate-out {
0% {
opacity: 1;
transform: scale(1) translateY(0);
}
100% {
opacity: 0;
transform: scale(0.95) translateY(-2px);
}
}
.context-menu-content[data-state="open"] {
animation: context-menu-animate-in 150ms ease-out forwards;
}
@keyframes context-menu-animate-in {
0% {
opacity: 0;
transform: scale(0.95) translateY(-2px);
}
100% {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.context-menu-item {
display: flex;
align-items: center;
padding: 8px 12px;
border-radius: calc(0.5rem - 0.25rem);
color: var(--secondary-color-4);
cursor: pointer;
font-size: 14px;
outline: none;
transition: background-color 100ms ease-out;
user-select: none;
}
.context-menu-item[data-disabled="true"] {
color: var(--secondary-color-5);
cursor: not-allowed;
}
.context-menu-item:hover:not([data-disabled="true"]),
.context-menu-item:focus-visible {
background: var(--light, var(--primary-color-4))
var(--dark, var(--primary-color-7));
color: var(--light, var(--secondary-color-1))
var(--dark, var(--secondary-color-4));
}
+26 -27
View File
@@ -9,18 +9,22 @@ use crate::{
}; };
#[derive(PartialEq, Clone, Props)] #[derive(PartialEq, Clone, Props)]
pub struct MediaGridProps pub struct MediaGridProps {
{
pub query: Signal<String>, pub query: Signal<String>,
pub max_page: Signal<i32>, pub max_page: Signal<i32>,
pub total_items: Signal<i32>, pub total_items: Signal<i32>,
pub page: Signal<i32>, pub page: Signal<i32>,
pub page_size: Signal<i32>, pub page_size: Signal<i32>,
pub on_page_loaded: Option<EventHandler<PaginationInfo>>,
}
pub struct PaginationInfo {
pub total_pages: i32,
pub total_items: i32,
} }
#[component] #[component]
pub fn MediaGrid(mut props: MediaGridProps) -> Element pub fn MediaGrid(props: MediaGridProps) -> Element {
{
let mut error_display = use_signal(|| { let mut error_display = use_signal(|| {
rsx! {} rsx! {}
}); });
@@ -34,32 +38,29 @@ pub fn MediaGrid(mut props: MediaGridProps) -> Element
query: Some(props.query.cloned()), query: Some(props.query.cloned()),
}; };
let result = client.list_media(request).await; let result = client.list_media(request).await;
if let Ok(items) = result if let Ok(items) = result {
{
let res = items.into_inner(); let res = items.into_inner();
return Ok(res); return Ok(res);
} } else {
else
{
let err = result.err().unwrap(); let err = result.err().unwrap();
let message = err.message(); let message = err.message();
return Err(format!("Failed to load results: {message}")); return Err(format!("Failed to load results: {message}"));
} }
})); }));
use_effect(move || match media_result() use_effect(move || match media_result() {
{ Some(value) => match value {
Some(value) => match value Ok(result) => {
{ if let Some(pagination) = result.pagination {
Ok(result) =>
{
if let Some(pagination) = result.pagination
{
let total_pages = pagination.total_pages; let total_pages = pagination.total_pages;
let total_items = pagination.total_items; let total_items = pagination.total_items;
props.max_page.set(total_pages.max(1)); if let Some(handler) = props.on_page_loaded {
props.total_items.set(total_items.max(1)); handler.call(PaginationInfo {
total_pages,
total_items,
});
}
} }
items.set(Some(result.items)); items.set(Some(result.items));
error_display.set(rsx! {}); error_display.set(rsx! {});
@@ -70,8 +71,7 @@ pub fn MediaGrid(mut props: MediaGridProps) -> Element
} }
}), }),
}, },
_ => _ => {}
{}
}); });
rsx! { rsx! {
@@ -87,8 +87,7 @@ pub fn MediaGrid(mut props: MediaGridProps) -> Element
} }
#[component] #[component]
fn PlaceholderGrid(count: usize) -> Element fn PlaceholderGrid(count: usize) -> Element {
{
rsx! { rsx! {
div{ div{
class: "mediaGrid", class: "mediaGrid",
@@ -100,12 +99,12 @@ fn PlaceholderGrid(count: usize) -> Element
} }
#[component] #[component]
fn MediaList(items: Vec<MediaModel>) -> Element fn MediaList(items: Vec<MediaModel>) -> Element {
{
rsx! { rsx! {
{items.iter().map(|itm| rsx!{ {items.iter().enumerate().map(|(index, itm)| rsx!{
MediaItem { MediaItem {
item: itm.clone() item: itm.clone(),
index
} }
})} })}
} }
+9 -7
View File
@@ -11,10 +11,19 @@ use crate::{
}, },
}; };
pub struct MediaClassChangeEvent
{
pub index: usize,
pub class: String,
}
#[derive(PartialEq, Clone, Props)] #[derive(PartialEq, Clone, Props)]
pub struct MediaItemProps pub struct MediaItemProps
{ {
pub item: MediaModel, pub item: MediaModel,
pub index: usize,
pub on_class_changed: Option<EventHandler<MediaClassChangeEvent>>,
pub on_deleted: Option<EventHandler<usize>>,
} }
#[component] #[component]
@@ -35,13 +44,6 @@ pub fn MediaItem(props: MediaItemProps) -> Element
let url = item.media_url; let url = item.media_url;
let download = format!("{HOST}{url}"); let download = format!("{HOST}{url}");
// class_signal.set(match class
// {
// 1 => "blur",
// 2 => "secret",
// _ => "",
// });
return rsx! { return rsx! {
ContextMenu{ ContextMenu{
ContextMenuTrigger{ ContextMenuTrigger{
+2 -2
View File
@@ -1,5 +1,5 @@
pub mod basic; pub mod basic;
// mod context_menu; mod context_menu;
mod icons; mod icons;
mod media_grid; mod media_grid;
mod media_item; mod media_item;
@@ -9,7 +9,7 @@ mod notif;
mod pagination; mod pagination;
mod passkey; mod passkey;
mod search; mod search;
// pub use context_menu::*; pub use context_menu::*;
pub use media_grid::*; pub use media_grid::*;
pub use media_item::*; pub use media_item::*;
pub use metrics_token::*; pub use metrics_token::*;
+15 -12
View File
@@ -2,8 +2,12 @@ use dioxus::prelude::*;
use web_sys::window; use web_sys::window;
#[component] #[component]
pub fn Pagination(page: Signal<i32>, max_page: Signal<i32>, item_count: Signal<i32>) -> Element pub fn Pagination(
{ page: Signal<i32>,
max_page: Signal<i32>,
item_count: Signal<i32>,
on_page_change: EventHandler<i32>,
) -> Element {
let cur_page_val = page.cloned(); let cur_page_val = page.cloned();
let max_page_val = max_page.cloned(); let max_page_val = max_page.cloned();
let item_count_val = item_count.cloned(); let item_count_val = item_count.cloned();
@@ -12,16 +16,16 @@ pub fn Pagination(page: Signal<i32>, max_page: Signal<i32>, item_count: Signal<i
class: "pagination", class: "pagination",
a { a {
onclick: move|_| { onclick: move|_| {
page.set(1); on_page_change.call(1);
on_page_change(); scroll_document();
}, },
"First" "First"
} }
a { a {
onclick: move|_| { onclick: move|_| {
let p = (cur_page_val - 1).max(1); let p = (cur_page_val - 1).max(1);
page.set(p); on_page_change.call(p);
on_page_change(); scroll_document();
}, },
"Prev" "Prev"
} }
@@ -29,15 +33,15 @@ pub fn Pagination(page: Signal<i32>, max_page: Signal<i32>, item_count: Signal<i
a { a {
onclick: move|_| { onclick: move|_| {
let p = (cur_page_val + 1).min(max_page_val); let p = (cur_page_val + 1).min(max_page_val);
page.set(p); on_page_change.call(p);
on_page_change(); scroll_document();
}, },
"Next" "Next"
} }
a { a {
onclick: move|_| { onclick: move|_| {
page.set(max_page_val); on_page_change.call(max_page_val);
on_page_change(); scroll_document();
}, },
"Last" "Last"
} }
@@ -45,8 +49,7 @@ pub fn Pagination(page: Signal<i32>, max_page: Signal<i32>, item_count: Signal<i
} }
} }
fn on_page_change() fn scroll_document() {
{
let window = window().expect("Failed to get window"); let window = window().expect("Failed to get window");
let document = window.document().expect("Failed to get document"); let document = window.document().expect("Failed to get document");
document document
+52 -5
View File
@@ -1,6 +1,11 @@
use dioxus::prelude::*; use dioxus::prelude::*;
use js_sys::{Uint8Array, wasm_bindgen::JsValue};
use web_sys::{
CredentialCreationOptions, PublicKeyCredentialCreationOptions, PublicKeyCredentialRpEntity,
PublicKeyCredentialUserEntity, window,
};
use crate::components::basic::Button; use crate::{components::basic::Button, rpc::aoba::PasskeyCredentialCreateOptions};
#[component] #[component]
pub fn PasskeyRegistrationButton() -> Element pub fn PasskeyRegistrationButton() -> Element
@@ -8,16 +13,58 @@ pub fn PasskeyRegistrationButton() -> Element
rsx! { rsx! {
Button{ Button{
text: "Register Passkey", text: "Register Passkey",
onclick: move |e| { onclick: move |_| {
start_passkey_registration();
} }
} }
} }
} }
fn start_passkey_registration() {} fn start_passkey_registration()
{
create_credential(todo!());
}
fn create_credential() {} fn create_credential(req_opts: PasskeyCredentialCreateOptions)
{
let window = window().expect("Window does not exist");
let credentaials = window.navigator().credentials();
let opts = opts_from_rpc(req_opts);
let result = credentaials.create_with_options(&opts);
todo!()
}
fn opts_from_rpc(rpc_opts: PasskeyCredentialCreateOptions) -> CredentialCreationOptions
{
let opt_user = &rpc_opts.user.expect("user is missing");
let opt_rp = &rpc_opts.rp.expect("rp is missing");
let opts = CredentialCreationOptions::new();
let rp = PublicKeyCredentialRpEntity::new(&opt_rp.name);
rp.set_id(&opt_rp.id);
let user = PublicKeyCredentialUserEntity::new_with_u8_array(
&opt_user.name,
&opt_user.display_name,
&to_u8_array(&opt_user.id),
);
let pub_key_opts = PublicKeyCredentialCreationOptions::new_with_u8_array(
&to_u8_array(&rpc_opts.challenge),
&JsValue::undefined(),
&rp,
&user,
);
//pub_key_opts.set_exclude_credentials(val);
opts.set_public_key(&pub_key_opts);
return opts;
}
fn to_u8_array(value: &String) -> Uint8Array
{
todo!()
}
#[component] #[component]
pub fn PasskeyLoginButton() -> Element pub fn PasskeyLoginButton() -> Element
+12 -2
View File
@@ -1,14 +1,24 @@
use dioxus::prelude::*; use dioxus::prelude::*;
#[component] #[component]
pub fn Search(query: Signal<String>, page: Signal<i32>) -> Element { pub fn Search(query: String, oninput: Option<EventHandler<String>>, onchange: Option<EventHandler<String>>) -> Element
{
rsx! { rsx! {
div { class: "searchBar", div { class: "searchBar",
input { input {
r#type: "search", r#type: "search",
placeholder: "Search Files", placeholder: "Search Files",
value: query, value: query,
oninput: move |event| {query.set(event.value()); page.set(1);}, oninput: move |event| {
if let Some(handler) = oninput {
handler.call(event.value());
}
},
onchange: move |event|{
if let Some(handler) = onchange {
handler.call(event.value());
}
}
} }
} }
} }
+13 -4
View File
@@ -8,7 +8,7 @@ pub mod rpc;
pub mod views; pub mod views;
use contexts::AuthContext; use contexts::AuthContext;
use dioxus::prelude::*; use dioxus::{prelude::*, router::RouterConfig};
use route::Route; use route::Route;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@@ -34,7 +34,6 @@ fn main()
fn App() -> Element fn App() -> Element
{ {
use_context_provider(|| AuthContext::new()); use_context_provider(|| AuthContext::new());
// use_context_provider(|| ContextMenuRenderer::default());
rsx! { rsx! {
document::Link { rel: "icon", href: FAVICON } document::Link { rel: "icon", href: FAVICON }
document::Link { rel: "preconnect", href: "https://fonts.googleapis.com" } document::Link { rel: "preconnect", href: "https://fonts.googleapis.com" }
@@ -46,7 +45,17 @@ fn App() -> Element
rel: "stylesheet", rel: "stylesheet",
href: "https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap", href: "https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap",
} }
Router::<Route> { config: || RouterConfig::default()
Router::<Route> { } .on_update(|state|{
match state.current() {
Route::Home {page, q} => {
info!("Page {}", page.unwrap_or(1));
return None;
// return Some(NavigationTarget::Internal(Route::Home { page, q }))
},
_ => None
}
})
}
} }
} }
+18 -1
View File
@@ -6,7 +6,9 @@ use tonic_web_wasm_client::Client;
use crate::{ use crate::{
RPC_HOST, RPC_HOST,
rpc::aoba::{auth_rpc_client::AuthRpcClient, metrics_rpc_client::MetricsRpcClient}, rpc::aoba::{
account_rpc_client::AccountRpcClient, auth_rpc_client::AuthRpcClient, metrics_rpc_client::MetricsRpcClient,
},
}; };
pub mod aoba pub mod aoba
@@ -17,6 +19,7 @@ pub mod aoba
static RPC_CLIENT: RpcConnection = RpcConnection { static RPC_CLIENT: RpcConnection = RpcConnection {
aoba: RwLock::new(None), aoba: RwLock::new(None),
auth: RwLock::new(None), auth: RwLock::new(None),
account: RwLock::new(None),
metrics: RwLock::new(None), metrics: RwLock::new(None),
jwt: RwLock::new(None), jwt: RwLock::new(None),
}; };
@@ -26,6 +29,7 @@ pub struct RpcConnection
{ {
aoba: RwLock<Option<AobaRpcClient<InterceptedService<Client, AuthInterceptor>>>>, aoba: RwLock<Option<AobaRpcClient<InterceptedService<Client, AuthInterceptor>>>>,
auth: RwLock<Option<AuthRpcClient<Client>>>, auth: RwLock<Option<AuthRpcClient<Client>>>,
account: RwLock<Option<AccountRpcClient<InterceptedService<Client, AuthInterceptor>>>>,
metrics: RwLock<Option<MetricsRpcClient<InterceptedService<Client, AuthInterceptor>>>>, metrics: RwLock<Option<MetricsRpcClient<InterceptedService<Client, AuthInterceptor>>>>,
jwt: RwLock<Option<String>>, jwt: RwLock<Option<String>>,
} }
@@ -38,6 +42,12 @@ impl RpcConnection
return self.aoba.read().unwrap().clone().unwrap(); return self.aoba.read().unwrap().clone().unwrap();
} }
pub fn get_account_client(&self) -> AccountRpcClient<InterceptedService<Client, AuthInterceptor>>
{
self.ensure_client();
return self.account.read().unwrap().clone().unwrap();
}
pub fn get_auth_client(&self) -> AuthRpcClient<Client> pub fn get_auth_client(&self) -> AuthRpcClient<Client>
{ {
self.ensure_client(); self.ensure_client();
@@ -58,6 +68,8 @@ impl RpcConnection
let aoba_client = AobaRpcClient::with_interceptor(wasm_client.clone(), AuthInterceptor); let aoba_client = AobaRpcClient::with_interceptor(wasm_client.clone(), AuthInterceptor);
*self.aoba.write().unwrap() = Some(aoba_client); *self.aoba.write().unwrap() = Some(aoba_client);
*self.auth.write().unwrap() = Some(AuthRpcClient::new(wasm_client.clone())); *self.auth.write().unwrap() = Some(AuthRpcClient::new(wasm_client.clone()));
*self.account.write().unwrap() =
Some(AccountRpcClient::with_interceptor(wasm_client.clone(), AuthInterceptor));
*self.metrics.write().unwrap() = *self.metrics.write().unwrap() =
Some(MetricsRpcClient::with_interceptor(wasm_client.clone(), AuthInterceptor)); Some(MetricsRpcClient::with_interceptor(wasm_client.clone(), AuthInterceptor));
} }
@@ -90,6 +102,11 @@ pub fn get_auth_rpc_client() -> AuthRpcClient<Client>
return RPC_CLIENT.get_auth_client(); return RPC_CLIENT.get_auth_client();
} }
pub fn get_account_rpc_client() -> AccountRpcClient<InterceptedService<Client, AuthInterceptor>>
{
return RPC_CLIENT.get_account_client();
}
pub fn get_metrics_rpc_client() -> MetricsRpcClient<InterceptedService<Client, AuthInterceptor>> pub fn get_metrics_rpc_client() -> MetricsRpcClient<InterceptedService<Client, AuthInterceptor>>
{ {
return RPC_CLIENT.get_metrics_client(); return RPC_CLIENT.get_metrics_client();
+32 -9
View File
@@ -1,5 +1,8 @@
use crate::components::{MediaGrid, Pagination, Search}; use crate::{
use dioxus::prelude::*; components::{MediaGrid, Pagination, PaginationInfo, Search},
route::Route,
};
use dioxus::{prelude::*, router::RouterConfig};
// #[component] // #[component]
// pub fn Home() -> Element // pub fn Home() -> Element
@@ -21,17 +24,37 @@ use dioxus::prelude::*;
#[component] #[component]
pub fn Home(page: Option<i32>, q: Option<String>) -> Element pub fn Home(page: Option<i32>, q: Option<String>) -> Element
{ {
let query = use_signal(|| q.unwrap_or("".to_string())); let mut query = use_signal(|| q.unwrap_or("".to_string()));
let page = use_signal(|| page.unwrap_or(1)); let mut page = use_signal(|| page.unwrap_or(1));
let page_size = use_signal::<i32>(|| 100); let page_size = use_signal::<i32>(|| 100);
let max_page = use_signal(|| 1 as i32); let mut max_page = use_signal(|| 1 as i32);
let item_count = use_signal(|| 0 as i32); let mut item_count = use_signal(|| 0 as i32);
rsx! { rsx! {
div { div {
class: "stickyTop", class: "stickyTop",
Search { query, page }, Search {
Pagination { page, max_page, item_count }, query: query(),
oninput: move |q| {
query.set(q);
page.set(1);
},
onchange: move |_|{
router().push(format!("/?page={}&q={}", page(), query()));
}
},
Pagination {
page, max_page, item_count,
on_page_change: move |p|{
page.set(p);
router().push(format!("/?page={}&q={}", page(), query()));
}
},
}
MediaGrid { query: query, page: page, max_page, total_items: item_count, page_size,
on_page_loaded: move |p: PaginationInfo| {
max_page.set(p.total_pages);
item_count.set(p.total_items);
}
} }
MediaGrid { query: query, page: page, max_page, total_items: item_count, page_size }
} }
} }
+2 -1
View File
@@ -2,7 +2,7 @@ use dioxus::prelude::*;
use tonic::IntoRequest; use tonic::IntoRequest;
use crate::{ use crate::{
components::{basic::Input, Notif, NotifType}, components::{Notif, NotifType, PasskeyLoginButton, basic::Input},
contexts::AuthContext, contexts::AuthContext,
rpc::{aoba::Credentials, get_auth_rpc_client}, rpc::{aoba::Credentials, get_auth_rpc_client},
}; };
@@ -72,6 +72,7 @@ pub fn Login() -> Element {
required: true, required: true,
} }
button { onclick: login, "Login!" } button { onclick: login, "Login!" }
PasskeyLoginButton {}
} }
} }
} }
+8 -18
View File
@@ -1,6 +1,4 @@
use crate::HOST; use crate::HOST;
use crate::components::radio_group::{RadioGroup, RadioItem};
use crate::rpc::aoba::SetMediaClassRequest;
use crate::rpc::{ use crate::rpc::{
aoba::{Id, MediaModel}, aoba::{Id, MediaModel},
get_rpc_client, get_rpc_client,
@@ -8,26 +6,20 @@ use crate::rpc::{
use dioxus::prelude::*; use dioxus::prelude::*;
#[component] #[component]
pub fn Media(id: String) -> Element pub fn Media(id: String) -> Element {
{
let media_result = use_resource(use_reactive!(|(id)| async move { let media_result = use_resource(use_reactive!(|(id)| async move {
let mut client = get_rpc_client(); let mut client = get_rpc_client();
let result = client.get_media(Id { value: id.clone() }).await; let result = client.get_media(Id { value: id.clone() }).await;
if let Ok(item) = result if let Ok(item) = result {
{
let res = item.into_inner(); let res = item.into_inner();
return res.value; return res.value;
} } else {
else
{
return None; return None;
} }
})); }));
return match media_result.cloned().unwrap_or(None) return match media_result.cloned().unwrap_or(None) {
{ Some(media) => {
Some(media) =>
{
return rsx! {MediaPage{media: media}}; return rsx! {MediaPage{media: media}};
} }
None => rsx! {"Not Found"}, None => rsx! {"Not Found"},
@@ -35,12 +27,10 @@ pub fn Media(id: String) -> Element
} }
#[component] #[component]
fn MediaPage(media: MediaModel) -> Element fn MediaPage(media: MediaModel) -> Element {
{
let url = media.thumb_url; let url = media.thumb_url;
let id = media.id.expect("Media has no id").value.clone(); // let id = media.id.expect("Media has no id").value.clone();
let cur_class = use_signal(|| match media.class let cur_class = use_signal(|| match media.class {
{
0 => "Standard", 0 => "Standard",
1 => "NSFW", 1 => "NSFW",
2 => "Secret", 2 => "Secret",
+6 -5
View File
@@ -8,15 +8,16 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="FFMpegCore" Version="5.4.0" /> <PackageReference Include="FFMpegCore" Version="5.4.0" />
<PackageReference Include="Fido2.Models" Version="4.0.1" />
<PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0" /> <PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.3" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.5" />
<PackageReference Include="MaybeError" Version="1.2.0" /> <PackageReference Include="MaybeError" Version="1.2.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.3" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.5" />
<PackageReference Include="MongoDB.Driver" Version="3.6.0" /> <PackageReference Include="MongoDB.Driver" Version="3.7.1" />
<PackageReference Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="3.0.0" /> <PackageReference Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="3.0.0" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.7" /> <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.7" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.16.0" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.17.0" />
<PackageReference Include="ZLinq" Version="1.5.5" /> <PackageReference Include="ZLinq" Version="1.5.6" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+4 -1
View File
@@ -1,4 +1,6 @@
using Microsoft.IdentityModel.Tokens; using Fido2NetLib.Objects;
using Microsoft.IdentityModel.Tokens;
using MongoDB.Bson; using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Attributes;
@@ -19,6 +21,7 @@ public class User
public bool IsArgon { get; set; } public bool IsArgon { get; set; }
public ObjectId[] ApiKeys { get; set; } = []; public ObjectId[] ApiKeys { get; set; } = [];
public List<ObjectId> RegTokens { get; set; } = []; public List<ObjectId> RegTokens { get; set; } = [];
public List<PublicKeyCredentialDescriptor> CredentialDescriptors { get; set; } = [];
public ClaimsIdentity GetIdentity() public ClaimsIdentity GetIdentity()
{ {
+10 -3
View File
@@ -1,5 +1,7 @@
using AobaCore.Models; using AobaCore.Models;
using Fido2NetLib.Objects;
using Isopoh.Cryptography.Argon2; using Isopoh.Cryptography.Argon2;
using MongoDB.Bson; using MongoDB.Bson;
@@ -54,13 +56,18 @@ public class AccountsService(IMongoDatabase db)
/* Get the salt */ /* Get the salt */
byte[] salt = new byte[16]; byte[] salt = new byte[16];
Array.Copy(hashBytes, 0, salt, 0, 16); Array.Copy(hashBytes, 0, salt, 0, 16);
/* Compute the hash on the password the user entered */
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000, HashAlgorithmName.SHA1); var hash= Rfc2898DeriveBytes.Pbkdf2(password, salt, 10000, HashAlgorithmName.SHA1, 20);
byte[] hash = pbkdf2.GetBytes(20);
/* Compare the results */ /* Compare the results */
for (int i = 0; i < 20; i++) for (int i = 0; i < 20; i++)
if (hashBytes[i + 16] != hash[i]) if (hashBytes[i + 16] != hash[i])
return false; return false;
return true; return true;
} }
public async Task<List<PublicKeyCredentialDescriptor>> GetPublicKeyCredentialDescriptorsAsync(ObjectId id, CancellationToken cancellationToken = default)
{
var creds = await _users.Find(u => u.Id == id).Project(u => u.CredentialDescriptors).FirstOrDefaultAsync(cancellationToken);
return creds ?? [];
}
} }
+7 -7
View File
@@ -9,22 +9,22 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Fido2" Version="4.0.0" /> <PackageReference Include="Fido2.AspNet" Version="4.0.1" />
<PackageReference Include="Grpc.AspNetCore" Version="2.76.0" /> <PackageReference Include="Grpc.AspNetCore" Version="2.76.0" />
<PackageReference Include="Grpc.AspNetCore.Web" Version="2.76.0" /> <PackageReference Include="Grpc.AspNetCore.Web" Version="2.76.0" />
<PackageReference Include="Grpc.Tools" Version="2.78.0"> <PackageReference Include="Grpc.Tools" Version="2.80.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0" /> <PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.5" />
<PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.16.0" /> <PackageReference Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.17.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.23.0" />
<PackageReference Include="MimeTypesMap" Version="1.0.9" /> <PackageReference Include="MimeTypesMap" Version="1.0.9" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.0" /> <PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.2" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" /> <PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.0" /> <PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.2" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.0" /> <PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.1" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.0" /> <PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.0" />
</ItemGroup> </ItemGroup>
+10
View File
@@ -121,6 +121,16 @@ builder.Services.AddAuthentication(options =>
builder.Services.AddAoba(); builder.Services.AddAoba();
builder.Services.AddFido2(opts =>
{
opts.ServerName = "Aoba";
opts.ServerDomain = "aoba.app";
#if DEBUG
opts.Origins = new HashSet<string> { "http://localhost:8081", "http://127.0.0.1:8080" };
#else
opts.Origins = new HashSet<string> { "https://aoba.app" };
#endif
});
#if DEBUG #if DEBUG
builder.Services.AddHostedService<DebugService>(); builder.Services.AddHostedService<DebugService>();
#endif #endif
+2 -2
View File
@@ -7,7 +7,7 @@ import "google/protobuf/empty.proto";
import "Proto/Types.proto"; import "Proto/Types.proto";
service AccountRpc { service AccountRpc {
rpc RegisterPasskey(google.protobuf.Empty) returns (PasskeyRegistrationCreds); rpc RegisterPasskey(google.protobuf.Empty) returns (PasskeyCredentialCreateOptions);
rpc CompletePasskeyRegistration(PasskeyPublicKey) returns (google.protobuf.Empty); rpc CompletePasskeyRegistration(PasskeyRegistrationCredentials) returns (google.protobuf.Empty);
} }
+40 -3
View File
@@ -121,9 +121,46 @@ message PasskeyPayload {
} }
message PasskeyRegistrationCreds{ message PasskeyCredentialCreateOptions{
string challenge = 1;
PublicKeyCredentialUser user = 2;
PublicKeyCredentialRpEntity rp = 3;
repeated PubKeyCredParam pubKeyParams = 4;
} }
message PasskeyPublicKey{
message PubKeyCredParam{
string alg = 1;
string type = 2;
} }
message PublicKeyCredentialRpEntity{
string id = 1;
string icon = 2;
string name = 3;
}
message PublicKeyCredentialUser{
string id = 1;
string name = 2;
string displayName = 3;
}
message PasskeyRegistrationCredentials{
string id = 1;
string rawId = 2;
string type = 3;
CredentialsClientResponse response = 4;
}
message CredentialsClientResponse{
string clientDataJSON = 1;
string attestationObject = 2;
string authenticatorData = 3;
}
message PublicKeyCredentialDescriptor{
string type = 1;
string id = 2;
repeated string transports = 3;
}
+34 -4
View File
@@ -1,20 +1,50 @@
using Aoba.RPC; using Aoba.RPC;
using Aoba.RPC.Account; using Aoba.RPC.Account;
using AobaCore.Services;
using AobaServer.Utils;
using Fido2NetLib;
using Google.Protobuf.WellKnownTypes; using Google.Protobuf.WellKnownTypes;
using Grpc.Core; using Grpc.Core;
using Isopoh.Cryptography.Argon2;
namespace AobaServer.Services; namespace AobaServer.Services;
public class AccountRpcService : AccountRpc.AccountRpcBase public class AccountRpcService(IFido2 fido2, AccountsService accounts) : AccountRpc.AccountRpcBase
{ {
public override Task<PasskeyRegistrationCreds> RegisterPasskey(Empty request, ServerCallContext context) public override async Task<PasskeyCredentialCreateOptions> RegisterPasskey(Empty request, ServerCallContext context)
{ {
return base.RegisterPasskey(request, context); var curUser = await accounts.GetUserAsync(context.GetUserId(), context.CancellationToken);
if (curUser == null)
throw new Exception($"Logged in user does not exist somehow. Id: {context.GetUserId()}");
var user = new Fido2User
{
DisplayName = curUser.Username,
Id = curUser.Id.ToByteArray(),
Name = curUser.Username
};
var credOptions = fido2.RequestNewCredential(new RequestNewCredentialParams
{
User = user,
ExcludeCredentials = curUser.CredentialDescriptors,
AuthenticatorSelection = new AuthenticatorSelection
{
ResidentKey = Fido2NetLib.Objects.ResidentKeyRequirement.Required,
UserVerification = Fido2NetLib.Objects.UserVerificationRequirement.Preferred
}
});
return credOptions.ToRPC();
} }
public override Task<Empty> CompletePasskeyRegistration(PasskeyPublicKey request, ServerCallContext context) public override Task<Empty> CompletePasskeyRegistration(PasskeyRegistrationCredentials request, ServerCallContext context)
{ {
return base.CompletePasskeyRegistration(request, context); return base.CompletePasskeyRegistration(request, context);
} }
+55
View File
@@ -0,0 +1,55 @@
using Aoba.RPC;
using Isopoh.Cryptography.Argon2;
namespace AobaServer.Utils;
public static class PasskeyExtensions
{
public static PublicKeyCredentialRpEntity ToRPC(this Fido2NetLib.PublicKeyCredentialRpEntity value)
{
return new PublicKeyCredentialRpEntity
{
Id = value.Id,
Icon = value.Icon,
Name = value.Name,
};
}
public static PublicKeyCredentialUser ToRPC(this Fido2NetLib.Fido2User value)
{
return new PublicKeyCredentialUser
{
Id = value.Id.ToB64String(),
DisplayName = value.DisplayName,
Name = value.Name,
};
}
public static PubKeyCredParam ToRPC(this Fido2NetLib.PubKeyCredParam value)
{
return new PubKeyCredParam
{
Alg = value.Alg.ToString(),
Type = value.Type.ToString(),
};
}
public static IEnumerable<PubKeyCredParam> ToRPC(this IEnumerable<Fido2NetLib.PubKeyCredParam> value)
{
return value.Select(x => x.ToRPC());
}
public static PasskeyCredentialCreateOptions ToRPC(this Fido2NetLib.CredentialCreateOptions value)
{
var opts = new PasskeyCredentialCreateOptions
{
Challenge = value.Challenge.ToB64String(),
Rp = value.Rp.ToRPC(),
User = value.User.ToRPC()
};
//todo: excluded credentials
opts.PubKeyParams.AddRange(value.PubKeyCredParams.ToRPC());
return opts;
}
}