添加用户auth相关功能
Some checks failed
build / build-rust (push) Failing after 1m40s

调整草稿和发布数据接口(需要user_id的从context中获取用户信息)
This commit is contained in:
soul-walker 2024-09-23 13:24:14 +08:00
parent c1a4a3701e
commit a0967e7583
18 changed files with 800 additions and 70 deletions

View File

@ -18,6 +18,7 @@
"protoc", "protoc",
"protos", "protos",
"repr", "repr",
"reqwest",
"rtss", "rtss",
"sqlx", "sqlx",
"sysinfo", "sysinfo",

386
Cargo.lock generated
View File

@ -288,6 +288,12 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.3.0" version = "1.3.0"
@ -776,6 +782,16 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "core-foundation"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.7" version = "0.8.7"
@ -1072,6 +1088,21 @@ 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.1" version = "1.2.1"
@ -1213,6 +1244,25 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
[[package]]
name = "h2"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205"
dependencies = [
"atomic-waker",
"bytes",
"fnv",
"futures-core",
"futures-sink",
"http",
"indexmap",
"slab",
"tokio",
"tokio-util",
"tracing",
]
[[package]] [[package]]
name = "handlebars" name = "handlebars"
version = "5.1.2" version = "5.1.2"
@ -1377,6 +1427,7 @@ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"h2",
"http", "http",
"http-body", "http-body",
"httparse", "httparse",
@ -1385,6 +1436,40 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"smallvec", "smallvec",
"tokio", "tokio",
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
dependencies = [
"futures-util",
"http",
"hyper",
"hyper-util",
"rustls",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tower-service",
]
[[package]]
name = "hyper-tls"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [
"bytes",
"http-body-util",
"hyper",
"hyper-util",
"native-tls",
"tokio",
"tokio-native-tls",
"tower-service",
] ]
[[package]] [[package]]
@ -1394,12 +1479,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel",
"futures-util", "futures-util",
"http", "http",
"http-body", "http-body",
"hyper", "hyper",
"pin-project-lite", "pin-project-lite",
"socket2",
"tokio", "tokio",
"tower",
"tower-service",
"tracing",
] ]
[[package]] [[package]]
@ -1452,6 +1542,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "ipnet"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4"
[[package]] [[package]]
name = "is_terminal_polyfill" name = "is_terminal_polyfill"
version = "1.70.1" version = "1.70.1"
@ -1649,6 +1745,23 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
[[package]]
name = "native-tls"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
dependencies = [
"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"
@ -1746,6 +1859,50 @@ version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "openssl"
version = "0.10.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1"
dependencies = [
"bitflags 2.6.0",
"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",
]
[[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.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]] [[package]]
name = "ordered-multimap" name = "ordered-multimap"
version = "0.6.0" version = "0.6.0"
@ -2143,6 +2300,64 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
[[package]]
name = "reqwest"
version = "0.12.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
dependencies = [
"base64 0.22.1",
"bytes",
"encoding_rs",
"futures-core",
"futures-util",
"h2",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-rustls",
"hyper-tls",
"hyper-util",
"ipnet",
"js-sys",
"log",
"mime",
"native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls-pemfile",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper 1.0.1",
"system-configuration",
"tokio",
"tokio-native-tls",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"windows-registry",
]
[[package]]
name = "ring"
version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
dependencies = [
"cc",
"cfg-if",
"getrandom",
"libc",
"spin",
"untrusted",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "ron" name = "ron"
version = "0.8.1" version = "0.8.1"
@ -2187,6 +2402,7 @@ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"bevy_ecs", "bevy_ecs",
"chrono", "chrono",
"reqwest",
"rtss_db", "rtss_db",
"rtss_dto", "rtss_dto",
"rtss_log", "rtss_log",
@ -2318,6 +2534,46 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "rustls"
version = "0.23.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
dependencies = [
"once_cell",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pemfile"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
dependencies = [
"base64 0.22.1",
"rustls-pki-types",
]
[[package]]
name = "rustls-pki-types"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
[[package]]
name = "rustls-webpki"
version = "0.102.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.17" version = "1.0.17"
@ -2330,12 +2586,44 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "schannel"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b"
dependencies = [
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.2.0" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
dependencies = [
"bitflags 2.6.0",
"core-foundation",
"core-foundation-sys",
"libc",
"security-framework-sys",
]
[[package]]
name = "security-framework-sys"
version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.210" version = "1.0.210"
@ -2787,6 +3075,9 @@ name = "sync_wrapper"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
dependencies = [
"futures-core",
]
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
@ -2802,6 +3093,27 @@ dependencies = [
"windows", "windows",
] ]
[[package]]
name = "system-configuration"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
dependencies = [
"bitflags 2.6.0",
"core-foundation",
"system-configuration-sys",
]
[[package]]
name = "system-configuration-sys"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.12.0" version = "3.12.0"
@ -2896,6 +3208,27 @@ dependencies = [
"syn", "syn",
] ]
[[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]]
name = "tokio-rustls"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
dependencies = [
"rustls",
"rustls-pki-types",
"tokio",
]
[[package]] [[package]]
name = "tokio-stream" name = "tokio-stream"
version = "0.1.15" version = "0.1.15"
@ -3093,6 +3426,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "try-lock"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]] [[package]]
name = "tungstenite" name = "tungstenite"
version = "0.21.0" version = "0.21.0"
@ -3169,6 +3508,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.2" version = "2.5.2"
@ -3219,6 +3564,15 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "want"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
dependencies = [
"try-lock",
]
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@ -3376,7 +3730,7 @@ checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
dependencies = [ dependencies = [
"windows-implement", "windows-implement",
"windows-interface", "windows-interface",
"windows-result", "windows-result 0.1.2",
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
@ -3402,6 +3756,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "windows-registry"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0"
dependencies = [
"windows-result 0.2.0",
"windows-strings",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.1.2" version = "0.1.2"
@ -3411,6 +3776,25 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "windows-result"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result 0.2.0",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"

View File

@ -5,3 +5,9 @@ port = 8765
[log] [log]
level = "debug" level = "debug"
[sso]
base_url = "http://localhost:8080"
login_url = "/api/login"
logout_url = "/api/login/logout"
user_info_url = "/api/login/getUserInfo"

View File

@ -1,2 +1,5 @@
[database] [database]
url = "postgresql://joylink:Joylink@0503@localhost:5432/joylink" url = "postgresql://joylink:Joylink@0503@localhost:5432/joylink"
[sso]
base_url = "https://joylink.club/jlcloud"

View File

@ -2,4 +2,7 @@
url = "postgresql://joylink:Joylink@0503@10.11.11.2:5432/joylink" url = "postgresql://joylink:Joylink@0503@10.11.11.2:5432/joylink"
[log] [log]
level = "info" level = "debug"
[sso]
base_url = "http://192.168.33.233/rtss-server"

View File

@ -17,6 +17,7 @@ async-graphql = { version = "7.0.7", features = ["chrono", "dataloader"] }
async-graphql-axum = "7.0.6" async-graphql-axum = "7.0.6"
base64 = "0.22.1" base64 = "0.22.1"
sysinfo = "0.31.3" sysinfo = "0.31.3"
reqwest = { version = "0.12.7", features = ["json"] }
bevy_ecs = { workspace = true } bevy_ecs = { workspace = true }
rtss_log = { path = "../rtss_log" } rtss_log = { path = "../rtss_log" }

View File

@ -8,11 +8,13 @@ use rtss_db::RtssDbAccessor;
use rtss_dto::common::DataType; use rtss_dto::common::DataType;
use serde_json::Value; use serde_json::Value;
use crate::apis::PageQueryDto; use crate::apis::{PageDto, PageQueryDto};
use super::common::{DataOptions, IscsDataOptions}; use super::common::{DataOptions, IscsDataOptions};
use super::release_data::ReleaseDataId; use super::release_data::ReleaseDataId;
use super::{PageDto, RtssDbLoader}; use crate::RtssDbLoader;
use crate::user_auth::{Role, RoleGuard, UserInfoDto};
#[derive(Default)] #[derive(Default)]
pub struct DraftDataQuery; pub struct DraftDataQuery;
@ -23,6 +25,7 @@ pub struct DraftDataMutation;
#[Object] #[Object]
impl DraftDataQuery { impl DraftDataQuery {
/// 分页查询所有草稿数据(系统管理用) /// 分页查询所有草稿数据(系统管理用)
#[graphql(guard = "RoleGuard::new(Role::Admin)")]
async fn draft_data_paging<'ctx>( async fn draft_data_paging<'ctx>(
&self, &self,
ctx: &Context<'ctx>, ctx: &Context<'ctx>,
@ -36,20 +39,24 @@ impl DraftDataQuery {
Ok(paging_result.into()) Ok(paging_result.into())
} }
/// 分页查询用户的草稿ISCS数据 /// 分页查询用户的草稿ISCS数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn user_draft_iscs_data_paging<'ctx>( async fn user_draft_iscs_data_paging<'ctx>(
&self, &self,
ctx: &Context<'ctx>, ctx: &Context<'ctx>,
paging: PageQueryDto, paging: PageQueryDto,
mut query: UserDraftDataFilterDto<IscsDataOptions>, mut query: UserDraftDataFilterDto<IscsDataOptions>,
) -> async_graphql::Result<PageDto<DraftIscsDataDto>> { ) -> async_graphql::Result<PageDto<DraftIscsDataDto>> {
let db_accessor = ctx.data::<RtssDbAccessor>()?; let user = ctx.data::<UserInfoDto>()?;
query.user_id = user.id_i32();
query.data_type = Some(DataType::Iscs); query.data_type = Some(DataType::Iscs);
let db_accessor = ctx.data::<RtssDbAccessor>()?;
let paging_result = db_accessor let paging_result = db_accessor
.query_draft_data(query.into(), paging.into()) .query_draft_data(query.into(), paging.into())
.await?; .await?;
Ok(paging_result.into()) Ok(paging_result.into())
} }
/// 分页查询共享的草稿ISCS数据 /// 分页查询共享的草稿ISCS数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn shared_draft_iscs_data_paging<'ctx>( async fn shared_draft_iscs_data_paging<'ctx>(
&self, &self,
ctx: &Context<'ctx>, ctx: &Context<'ctx>,
@ -64,18 +71,21 @@ impl DraftDataQuery {
Ok(paging_result.into()) Ok(paging_result.into())
} }
/// 根据id获取草稿数据 /// 根据id获取草稿数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn draft_data(&self, ctx: &Context<'_>, id: i32) -> async_graphql::Result<DraftDataDto> { async fn draft_data(&self, ctx: &Context<'_>, id: i32) -> async_graphql::Result<DraftDataDto> {
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let draft_data = db_accessor.query_draft_data_by_id(id).await?; let draft_data = db_accessor.query_draft_data_by_id(id).await?;
Ok(draft_data.into()) Ok(draft_data.into())
} }
/// 查询是否已经存在同一用户下的同名草稿数据 /// 查询是否已经存在同一用户下的同名草稿数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn draft_data_exist( async fn draft_data_exist(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
user_id: i32,
name: String, name: String,
) -> async_graphql::Result<bool> { ) -> async_graphql::Result<bool> {
let user = ctx.data::<UserInfoDto>()?;
let user_id = user.id_i32();
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let exist = db_accessor.is_draft_data_exist(user_id, &name).await?; let exist = db_accessor.is_draft_data_exist(user_id, &name).await?;
Ok(exist) Ok(exist)
@ -85,16 +95,20 @@ impl DraftDataQuery {
#[Object] #[Object]
impl DraftDataMutation { impl DraftDataMutation {
/// 创建草稿ISCS数据 /// 创建草稿ISCS数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn create_draft_iscs_data( async fn create_draft_iscs_data(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
input: CreateDraftDataDto<IscsDataOptions>, mut input: CreateDraftDataDto<IscsDataOptions>,
) -> async_graphql::Result<DraftIscsDataDto> { ) -> async_graphql::Result<DraftIscsDataDto> {
let user = ctx.data::<UserInfoDto>()?;
input = input.with_user_id(user.id_i32());
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let draft_data = db_accessor.create_draft_data(input.into()).await?; let draft_data = db_accessor.create_draft_data(input.into()).await?;
Ok(draft_data.into()) Ok(draft_data.into())
} }
/// 更新草稿数据name /// 更新草稿数据name
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn update_draft_data_name( async fn update_draft_data_name(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -107,6 +121,7 @@ impl DraftDataMutation {
} }
/// 更新草稿数据data /// 更新草稿数据data
/// data为base64编码的字符串 /// data为base64编码的字符串
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn update_draft_data_data( async fn update_draft_data_data(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -121,6 +136,7 @@ impl DraftDataMutation {
Ok(draft_data.into()) Ok(draft_data.into())
} }
/// 更新草稿数据共享状态 /// 更新草稿数据共享状态
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn update_draft_data_shared( async fn update_draft_data_shared(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -132,6 +148,7 @@ impl DraftDataMutation {
Ok(draft_data.into()) Ok(draft_data.into())
} }
/// 删除草稿数据 /// 删除草稿数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn delete_draft_data( async fn delete_draft_data(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -142,6 +159,7 @@ impl DraftDataMutation {
Ok(true) Ok(true)
} }
/// 设置草稿数据的默认发布数据 /// 设置草稿数据的默认发布数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn set_default_release_data_id( async fn set_default_release_data_id(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -155,13 +173,15 @@ impl DraftDataMutation {
Ok(draft_data.into()) Ok(draft_data.into())
} }
/// 草稿数据另存为 /// 草稿数据另存为
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn save_as_new_draft_data( async fn save_as_new_draft_data(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
id: i32, id: i32,
name: String, name: String,
user_id: i32,
) -> async_graphql::Result<DraftDataDto> { ) -> async_graphql::Result<DraftDataDto> {
let user = ctx.data::<UserInfoDto>()?;
let user_id = user.id_i32();
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let draft_data = db_accessor.save_as_new_draft(id, &name, user_id).await?; let draft_data = db_accessor.save_as_new_draft(id, &name, user_id).await?;
Ok(draft_data.into()) Ok(draft_data.into())
@ -173,12 +193,24 @@ impl DraftDataMutation {
pub struct CreateDraftDataDto<T: DataOptions> { pub struct CreateDraftDataDto<T: DataOptions> {
pub name: String, pub name: String,
pub options: Option<T>, pub options: Option<T>,
pub user_id: i32, #[graphql(skip)]
pub user_id: Option<i32>,
}
impl<T: DataOptions> CreateDraftDataDto<T> {
pub fn with_user_id(mut self, id: i32) -> Self {
self.user_id = Some(id);
self
}
} }
impl<T: DataOptions> From<CreateDraftDataDto<T>> for rtss_db::CreateDraftData { impl<T: DataOptions> From<CreateDraftDataDto<T>> for rtss_db::CreateDraftData {
fn from(value: CreateDraftDataDto<T>) -> Self { fn from(value: CreateDraftDataDto<T>) -> Self {
let cdd = Self::new(&value.name, DataType::Iscs, value.user_id); let cdd = Self::new(
&value.name,
DataType::Iscs,
value.user_id.expect("CreateDraftDataDto need user_id"),
);
if value.options.is_some() { if value.options.is_some() {
cdd.with_options(serde_json::to_value(value.options).unwrap()) cdd.with_options(serde_json::to_value(value.options).unwrap())
} else { } else {

View File

@ -1,11 +1,10 @@
use async_graphql::dataloader::DataLoader; use async_graphql::{Enum, InputObject, MergedObject, OutputType, SimpleObject};
use async_graphql::{EmptySubscription, MergedObject, Schema};
use async_graphql::{Enum, InputObject, OutputType, SimpleObject};
use draft_data::{DraftDataMutation, DraftDataQuery}; use draft_data::{DraftDataMutation, DraftDataQuery};
use release_data::{ReleaseDataMutation, ReleaseDataQuery}; use release_data::{ReleaseDataMutation, ReleaseDataQuery};
use crate::simulation_definition::MutexSimulationManager; mod simulation_definition;
use crate::ServerConfig; mod sys_info;
use simulation_definition::*;
mod common; mod common;
mod draft_data; mod draft_data;
@ -74,25 +73,3 @@ impl<T: OutputType, M: Into<T>> From<rtss_db::common::PageResult<M>> for PageDto
) )
} }
} }
pub struct RtssDbLoader {
pub(crate) db_accessor: rtss_db::RtssDbAccessor,
}
impl RtssDbLoader {
pub fn new(db_accessor: rtss_db::RtssDbAccessor) -> Self {
Self { db_accessor }
}
}
pub type RtssAppSchema = Schema<Query, Mutation, EmptySubscription>;
pub async fn new_schema(config: &ServerConfig) -> RtssAppSchema {
let dba = rtss_db::get_db_accessor(&config.database_url).await;
let loader = RtssDbLoader::new(dba.clone());
Schema::build(Query::default(), Mutation::default(), EmptySubscription)
.data(dba)
.data(DataLoader::new(loader, tokio::spawn))
.data(MutexSimulationManager::default())
.finish()
}

View File

@ -12,9 +12,12 @@ use rtss_dto::common::DataType;
use serde_json::Value; use serde_json::Value;
use crate::apis::draft_data::DraftDataDto; use crate::apis::draft_data::DraftDataDto;
use crate::RtssDbLoader;
use super::common::{DataOptions, IscsDataOptions}; use super::common::{DataOptions, IscsDataOptions};
use super::{PageDto, PageQueryDto, RtssDbLoader}; use super::{PageDto, PageQueryDto};
use crate::user_auth::{Role, RoleGuard, UserInfoDto};
#[derive(Default)] #[derive(Default)]
pub struct ReleaseDataQuery; pub struct ReleaseDataQuery;
@ -25,6 +28,7 @@ pub struct ReleaseDataMutation;
#[Object] #[Object]
impl ReleaseDataQuery { impl ReleaseDataQuery {
/// 分页查询所有发布数据(系统管理用) /// 分页查询所有发布数据(系统管理用)
#[graphql(guard = "RoleGuard::new(Role::Admin)")]
async fn release_data_paging( async fn release_data_paging(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -39,6 +43,7 @@ impl ReleaseDataQuery {
} }
/// 分页查询发布的ISCS数据 /// 分页查询发布的ISCS数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn release_iscs_data_paging( async fn release_iscs_data_paging(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -54,6 +59,7 @@ impl ReleaseDataQuery {
} }
/// id查询发布数据及当前使用的版本数据 /// id查询发布数据及当前使用的版本数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn release_data( async fn release_data(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -65,6 +71,7 @@ impl ReleaseDataQuery {
} }
/// 是否已经存在相同name的发布数据 /// 是否已经存在相同name的发布数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn is_release_data_name_exists( async fn is_release_data_name_exists(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -79,6 +86,7 @@ impl ReleaseDataQuery {
} }
/// 查询发布数据的版本 /// 查询发布数据的版本
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn release_data_version_paging( async fn release_data_version_paging(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -93,6 +101,7 @@ impl ReleaseDataQuery {
} }
/// 根据id获取发布数据版本详情 /// 根据id获取发布数据版本详情
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn release_data_version( async fn release_data_version(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -109,6 +118,7 @@ impl ReleaseDataQuery {
#[Object] #[Object]
impl ReleaseDataMutation { impl ReleaseDataMutation {
/// 发布到新的发布数据 /// 发布到新的发布数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn release_new_from_draft( async fn release_new_from_draft(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -116,28 +126,34 @@ impl ReleaseDataMutation {
name: String, name: String,
description: String, description: String,
) -> async_graphql::Result<ReleaseDataWithUsedVersionDto> { ) -> async_graphql::Result<ReleaseDataWithUsedVersionDto> {
let user = ctx.data::<UserInfoDto>()?;
let user_id = user.id_i32();
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let result = db_accessor let result = db_accessor
.release_new_from_draft(draft_id, &name, &description) .release_new_from_draft(draft_id, &name, &description, Some(user_id))
.await?; .await?;
Ok(result.into()) Ok(result.into())
} }
/// 发布到默认发布数据需要草稿数据发布过或设置了默认发布数据id /// 发布到默认发布数据需要草稿数据发布过或设置了默认发布数据id
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn release_to_default_release_data( async fn release_to_default_release_data(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
draft_id: i32, draft_id: i32,
description: String, description: String,
) -> async_graphql::Result<ReleaseDataWithUsedVersionDto> { ) -> async_graphql::Result<ReleaseDataWithUsedVersionDto> {
let user = ctx.data::<UserInfoDto>()?;
let user_id = user.id_i32();
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let result = db_accessor let result = db_accessor
.release_to_existing(draft_id, &description) .release_to_existing(draft_id, &description, Some(user_id))
.await?; .await?;
Ok(result.into()) Ok(result.into())
} }
/// 更新发布数据name /// 更新发布数据name
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn update_release_data_name( async fn update_release_data_name(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -150,6 +166,7 @@ impl ReleaseDataMutation {
} }
/// 上下架发布数据 /// 上下架发布数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn update_release_data_published( async fn update_release_data_published(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -164,6 +181,7 @@ impl ReleaseDataMutation {
} }
/// 更新发布数据使用的版本 /// 更新发布数据使用的版本
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn update_release_data_used_version( async fn update_release_data_used_version(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
@ -178,12 +196,14 @@ impl ReleaseDataMutation {
} }
/// 从发布数据版本中创建草稿数据 /// 从发布数据版本中创建草稿数据
#[graphql(guard = "RoleGuard::new(Role::User)")]
async fn create_draft_data_from_release_data_version( async fn create_draft_data_from_release_data_version(
&self, &self,
ctx: &Context<'_>, ctx: &Context<'_>,
version_id: i32, version_id: i32,
user_id: i32,
) -> async_graphql::Result<DraftDataDto> { ) -> async_graphql::Result<DraftDataDto> {
let user = ctx.data::<UserInfoDto>()?;
let user_id = user.id_i32();
let db_accessor = ctx.data::<RtssDbAccessor>()?; let db_accessor = ctx.data::<RtssDbAccessor>()?;
let result = db_accessor let result = db_accessor
.create_draft_from_release_version(version_id, user_id) .create_draft_from_release_version(version_id, user_id)

View File

@ -1,7 +1,7 @@
use async_graphql::{Context, InputObject, Object}; use async_graphql::{Context, InputObject, Object};
use rtss_sim_manage::{AvailablePlugins, SimulationBuilder}; use rtss_sim_manage::{AvailablePlugins, SimulationBuilder};
use crate::simulation_definition::{MutexSimulationManager, SimulationOperation}; use super::{MutexSimulationManager, SimulationOperation};
#[derive(Default)] #[derive(Default)]
pub struct SimulationQuery; pub struct SimulationQuery;

View File

@ -1,7 +1,6 @@
// mod jwt_auth; // mod jwt_auth;
mod apis; mod apis;
mod server; mod server;
mod simulation_definition; mod user_auth;
mod sys_info;
pub use server::*; pub use server::*;

View File

@ -8,16 +8,22 @@ use axum::{
routing::get, routing::get,
Router, Router,
}; };
use dataloader::DataLoader;
use http::{playground_source, GraphQLPlaygroundConfig}; use http::{playground_source, GraphQLPlaygroundConfig};
use rtss_log::tracing::{debug, info}; use rtss_log::tracing::{debug, info};
use tokio::net::TcpListener; use tokio::net::TcpListener;
use tower_http::cors::CorsLayer; use tower_http::cors::CorsLayer;
use crate::apis::RtssAppSchema; use crate::apis::{Mutation, Query};
use crate::user_auth;
pub use crate::user_auth::UserAuthClient;
#[derive(Clone)]
pub struct ServerConfig { pub struct ServerConfig {
pub database_url: String, pub database_url: String,
pub port: u16, pub port: u16,
pub user_auth_client: Option<user_auth::UserAuthClient>,
} }
impl ServerConfig { impl ServerConfig {
@ -25,16 +31,22 @@ impl ServerConfig {
Self { Self {
database_url: database_url.to_string(), database_url: database_url.to_string(),
port, port,
user_auth_client: None,
} }
} }
pub fn with_user_auth_client(mut self, user_auth_client: user_auth::UserAuthClient) -> Self {
self.user_auth_client = Some(user_auth_client);
self
}
pub fn to_socket_addr(&self) -> String { pub fn to_socket_addr(&self) -> String {
format!("0.0.0.0:{}", self.port) format!("0.0.0.0:{}", self.port)
} }
} }
pub async fn serve(config: ServerConfig) -> anyhow::Result<()> { pub async fn serve(config: ServerConfig) -> anyhow::Result<()> {
let schema = crate::apis::new_schema(&config).await; let schema = new_schema(config.clone()).await;
let app = Router::new() let app = Router::new()
.route("/", get(graphiql).post(graphql_handler)) .route("/", get(graphiql).post(graphql_handler))
@ -58,23 +70,45 @@ pub async fn serve(config: ServerConfig) -> anyhow::Result<()> {
async fn graphql_handler( async fn graphql_handler(
State(schema): State<RtssAppSchema>, State(schema): State<RtssAppSchema>,
_headers: HeaderMap, headers: HeaderMap,
req: GraphQLRequest, req: GraphQLRequest,
) -> GraphQLResponse { ) -> GraphQLResponse {
let req = req.into_inner(); let mut req = req.into_inner();
// let mut req = req.into_inner(); let token = user_auth::get_token_from_headers(&headers);
// let token = jwt_auth::get_token_from_headers(headers); if let Some(token) = token {
// match token { req = req.data(token);
// Ok(token) => { }
// req = req.data(token);
// }
// Err(e) => {
// error!("Error getting token from headers: {:?}", e);
// }
// }
schema.execute(req).await.into() schema.execute(req).await.into()
} }
async fn graphiql() -> impl IntoResponse { async fn graphiql() -> impl IntoResponse {
Html(playground_source(GraphQLPlaygroundConfig::new("/"))) Html(playground_source(GraphQLPlaygroundConfig::new("/")))
} }
pub struct RtssDbLoader {
pub(crate) db_accessor: rtss_db::RtssDbAccessor,
}
impl RtssDbLoader {
pub fn new(db_accessor: rtss_db::RtssDbAccessor) -> Self {
Self { db_accessor }
}
}
pub type RtssAppSchema = Schema<Query, Mutation, EmptySubscription>;
pub async fn new_schema(config: ServerConfig) -> RtssAppSchema {
let user_info_cache = crate::user_auth::UserAuthCache::new(
config
.user_auth_client
.expect("user auth client not configured"),
);
let dba = rtss_db::get_db_accessor(&config.database_url).await;
let loader = RtssDbLoader::new(dba.clone());
Schema::build(Query::default(), Mutation::default(), EmptySubscription)
.data(user_info_cache)
.data(dba)
.data(DataLoader::new(loader, tokio::spawn))
// .data(MutexSimulationManager::default())
.finish()
}

View File

@ -0,0 +1,247 @@
use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
use async_graphql::Guard;
use axum::http::HeaderMap;
use rtss_log::tracing::error;
use serde::{Deserialize, Serialize};
#[derive(Eq, PartialEq, Clone, Copy)]
pub enum Role {
Admin,
User,
}
pub struct RoleGuard {
role: Role,
}
impl RoleGuard {
pub fn new(role: Role) -> Self {
Self { role }
}
}
impl Guard for RoleGuard {
async fn check(&self, ctx: &async_graphql::Context<'_>) -> async_graphql::Result<()> {
if let Some(token) = ctx.data_opt::<Token>() {
// 从ctx中获取UserAuthCache, 从cache中获取用户信息
let user_auth_cache = ctx.data::<UserAuthCache>().unwrap();
let user_info = user_auth_cache.query_user(&token.0).await?;
// 判断用户角色
if user_info.roles().contains(&self.role) {
return Ok(());
}
}
Err(async_graphql::Error::new("Unauthorized"))
}
}
pub struct UserAuthCache {
// TODO: 使用 LRU 等缓存策略,而不是简单的 HashMap
cache: Arc<Mutex<HashMap<String, UserInfoDto>>>,
client: UserAuthClient,
}
impl UserAuthCache {
pub fn new(user_auth_client: UserAuthClient) -> Self {
Self {
cache: Arc::new(Mutex::new(HashMap::with_capacity(100))),
client: user_auth_client,
}
}
}
pub struct Token(pub String);
pub fn get_token_from_headers(headers: &HeaderMap) -> Option<Token> {
headers
.get("Token")
.and_then(|token| token.to_str().map(|s| Token(s.to_string())).ok())
}
#[derive(Debug, Clone)]
pub struct UserAuthClient {
pub base_url: String,
pub login_url: String,
pub logout_url: String,
pub user_info_url: String,
}
impl UserAuthClient {
#[allow(dead_code)]
async fn login(&self, login_info: LoginInfo) -> anyhow::Result<String> {
let url = format!("{}{}", self.base_url, self.login_url);
let response = reqwest::Client::new()
.post(&url)
.json(&login_info)
.send()
.await?;
let common = response.json::<CommonResponseDto>().await?;
if common.code != 200 {
// 记录详细日志
error!("Login failed with code {}: {}", common.code, common.message);
return Err(anyhow::anyhow!(common.message));
}
// 安全地处理 Option 类型
match common.data {
Some(data) => {
if let Some(token_str) = data.as_str() {
Ok(token_str.to_string())
} else {
// 记录详细日志
error!("Data is not a string");
Err(anyhow::anyhow!("Data is not a string"))
}
}
None => {
// 记录详细日志
error!("No data returned");
Err(anyhow::anyhow!("No data returned"))
}
}
}
async fn query_user_info(&self, token: &str) -> anyhow::Result<UserInfoDto> {
let url = format!("{}{}?token={token}", self.base_url, self.user_info_url);
let response = reqwest::get(url).await?;
let common = response.json::<CommonResponseDto>().await?;
if common.code != 200 {
return Err(anyhow::anyhow!(common.message));
}
let user_info = serde_json::from_value(common.data.unwrap())?;
Ok(user_info)
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct LoginInfo {
pub account: String,
pub password: String,
#[serde(rename = "clientId")]
pub client_id: String,
pub secret: String,
pub project: String,
}
impl Default for LoginInfo {
fn default() -> Self {
Self {
account: "17791995809".to_string(),
password: "e10adc3949ba59abbe56e057f20f883e".to_string(),
client_id: "1".to_string(),
secret: "joylink".to_string(),
project: "DEFAULT".to_string(),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CommonResponseDto {
pub code: i32,
pub message: String,
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserInfoDto {
pub id: String,
pub name: Option<String>,
pub nickname: Option<String>,
pub roles: Vec<String>,
}
impl UserInfoDto {
pub fn id_i32(&self) -> i32 {
self.id
.parse::<i32>()
.expect("parse UserInfoDto.id to i32 failed")
}
pub fn roles(&self) -> Vec<Role> {
self.roles
.iter()
.filter_map(|role| match role.as_str() {
"04" | "05" => Some(Role::Admin),
"01" | "03" => Some(Role::User),
_ => None,
})
.collect()
}
}
impl UserAuthCache {
#[allow(dead_code)]
pub fn len(&self) -> usize {
let cache = self.cache.lock().unwrap();
cache.len()
}
#[allow(dead_code)]
pub fn get_all(&self) -> HashMap<String, UserInfoDto> {
let cache = self.cache.lock().unwrap();
cache.clone()
}
fn insert(&self, key: String, value: UserInfoDto) {
let mut cache = self.cache.lock().unwrap();
cache.insert(key, value);
}
fn get(&self, key: &str) -> Option<UserInfoDto> {
let cache = self.cache.lock().unwrap();
cache.get(key).cloned()
}
pub async fn query_user(&self, token: &str) -> anyhow::Result<UserInfoDto> {
if let Some(user_info) = self.get(token) {
Ok(user_info)
} else {
let user_info = self.client.query_user_info(token).await?;
self.insert(token.to_string(), user_info.clone());
Ok(user_info)
}
}
}
#[cfg(test)]
mod tests {
use anyhow::Ok;
use rtss_log::tracing::Level;
use super::*;
#[test]
fn test_login_info_serialize() {
let login_info = LoginInfo::default();
let json = serde_json::to_string(&login_info).unwrap();
println!("{}", json);
assert_eq!(
json,
r#"{"account":"17791995809","password":"e10adc3949ba59abbe56e057f20f883e","clientId":"1","secret":"joylink","project":"DEFAULT"}"#
);
let login_info: LoginInfo = serde_json::from_str(&json).unwrap();
assert_eq!(login_info.account, "17791995809");
}
#[tokio::test]
async fn test_user_auth_cache() -> anyhow::Result<()> {
rtss_log::Logging::default().with_level(Level::DEBUG).init();
let cache = UserAuthCache::new(UserAuthClient {
base_url: "https://joylink.club/jlcloud".to_string(),
login_url: "/api/login".to_string(),
logout_url: "/api/login/logout".to_string(),
user_info_url: "/api/login/getUserInfo".to_string(),
});
let token = cache.client.login(LoginInfo::default()).await?;
let user = cache.query_user(&token).await?;
println!("token: {}, {:?}", token, user);
assert_eq!(cache.len(), 1);
Ok(())
}
}

View File

@ -21,12 +21,14 @@ pub trait ReleaseDataAccessor {
draft_id: i32, draft_id: i32,
name: &str, name: &str,
description: &str, description: &str,
user_id: Option<i32>,
) -> Result<(ReleaseDataModel, ReleaseDataVersionModel), DbAccessError>; ) -> Result<(ReleaseDataModel, ReleaseDataVersionModel), DbAccessError>;
/// 从草稿发布到已有草稿默认的release data的新version并使用新version /// 从草稿发布到已有草稿默认的release data的新version并使用新version
async fn release_to_existing( async fn release_to_existing(
&self, &self,
draft_id: i32, draft_id: i32,
description: &str, description: &str,
user_id: Option<i32>,
) -> Result<(ReleaseDataModel, ReleaseDataVersionModel), DbAccessError>; ) -> Result<(ReleaseDataModel, ReleaseDataVersionModel), DbAccessError>;
/// 分页查询发布数据列表 /// 分页查询发布数据列表
async fn query_release_data_list( async fn query_release_data_list(
@ -241,6 +243,7 @@ impl ReleaseDataAccessor for RtssDbAccessor {
draft_id: i32, draft_id: i32,
name: &str, name: &str,
description: &str, description: &str,
user_id: Option<i32>,
) -> Result<(ReleaseDataModel, ReleaseDataVersionModel), DbAccessError> { ) -> Result<(ReleaseDataModel, ReleaseDataVersionModel), DbAccessError> {
// 查询草稿数据 // 查询草稿数据
let draft = self.query_draft_data_by_id(draft_id).await?; let draft = self.query_draft_data_by_id(draft_id).await?;
@ -272,7 +275,7 @@ impl ReleaseDataAccessor for RtssDbAccessor {
.bind(name) .bind(name)
.bind(draft.data_type as i32) .bind(draft.data_type as i32)
.bind(draft.options.clone()) .bind(draft.options.clone())
.bind(draft.user_id) .bind(user_id.or(Some(draft.user_id)))
.fetch_one(&mut *tx) .fetch_one(&mut *tx)
.await?; .await?;
// 创建发布数据版本 // 创建发布数据版本
@ -283,7 +286,7 @@ impl ReleaseDataAccessor for RtssDbAccessor {
options: draft.options.clone(), options: draft.options.clone(),
data: draft.data.unwrap(), data: draft.data.unwrap(),
description: description.to_string(), description: description.to_string(),
user_id: draft.user_id, user_id: user_id.unwrap_or(draft.user_id),
}, },
&mut *tx, &mut *tx,
) )
@ -310,6 +313,7 @@ impl ReleaseDataAccessor for RtssDbAccessor {
&self, &self,
draft_id: i32, draft_id: i32,
description: &str, description: &str,
user_id: Option<i32>,
) -> Result<(ReleaseDataModel, ReleaseDataVersionModel), DbAccessError> { ) -> Result<(ReleaseDataModel, ReleaseDataVersionModel), DbAccessError> {
// 查询草稿数据 // 查询草稿数据
let draft = self.query_draft_data_by_id(draft_id).await?; let draft = self.query_draft_data_by_id(draft_id).await?;
@ -336,7 +340,7 @@ impl ReleaseDataAccessor for RtssDbAccessor {
options: draft.options.clone(), options: draft.options.clone(),
data: draft.data.unwrap(), data: draft.data.unwrap(),
description: description.to_string(), description: description.to_string(),
user_id: draft.user_id, user_id: user_id.unwrap_or(draft.user_id),
}, },
&mut *tx, &mut *tx,
) )
@ -709,7 +713,9 @@ mod tests {
let name = "test_release"; let name = "test_release";
let description = "test release"; let description = "test release";
// 发布到默认发布数据 // 发布到默认发布数据
let result = accessor.release_to_existing(draft.id, description).await; let result = accessor
.release_to_existing(draft.id, description, None)
.await;
assert!(result.is_err()); assert!(result.is_err());
if let Some(e) = result.err() { if let Some(e) = result.err() {
match e { match e {
@ -722,7 +728,7 @@ mod tests {
// 发布新版本 // 发布新版本
let (release_data, version1) = accessor let (release_data, version1) = accessor
.release_new_from_draft(draft.id, name, &description) .release_new_from_draft(draft.id, name, &description, None)
.await?; .await?;
assert_eq!(release_data.name, name); assert_eq!(release_data.name, name);
// 检查使用版本是刚刚发布的版本 // 检查使用版本是刚刚发布的版本
@ -752,7 +758,9 @@ mod tests {
let data = "test2".as_bytes(); let data = "test2".as_bytes();
accessor.update_draft_data_data(draft.id, data).await?; accessor.update_draft_data_data(draft.id, data).await?;
// 发布到已存在发布数据成功测试 // 发布到已存在发布数据成功测试
let (release_data, version2) = accessor.release_to_existing(draft.id, description).await?; let (release_data, version2) = accessor
.release_to_existing(draft.id, description, None)
.await?;
assert_eq!(release_data.name, name); assert_eq!(release_data.name, name);
// 检查使用版本是刚刚发布的版本 // 检查使用版本是刚刚发布的版本
assert_eq!(release_data.used_version_id, Some(version2.id)); assert_eq!(release_data.used_version_id, Some(version2.id));
@ -828,12 +836,12 @@ mod tests {
) )
.await?; .await?;
let (release_data, _) = accessor let (release_data, _) = accessor
.release_new_from_draft(draft.id, &name, &description) .release_new_from_draft(draft.id, &name, &description, None)
.await?; .await?;
assert_eq!(release_data.name, name); assert_eq!(release_data.name, name);
let another_name = format!("{}_another", name); let another_name = format!("{}_another", name);
let (release_data, _) = accessor let (release_data, _) = accessor
.release_new_from_draft(draft.id, &another_name, &description) .release_new_from_draft(draft.id, &another_name, &description, None)
.await?; .await?;
assert_eq!(release_data.name, another_name); assert_eq!(release_data.name, another_name);
} }

View File

@ -29,12 +29,22 @@ impl From<Log> for rtss_log::Logging {
} }
} }
#[derive(Debug, Deserialize)]
#[allow(unused)]
pub struct Sso {
pub base_url: String,
pub login_url: String,
pub logout_url: String,
pub user_info_url: String,
}
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
#[allow(unused)] #[allow(unused)]
pub struct AppConfig { pub struct AppConfig {
pub server: Server, pub server: Server,
pub log: Log, pub log: Log,
pub database: Database, pub database: Database,
pub sso: Sso,
} }
impl AppConfig { impl AppConfig {

View File

@ -31,10 +31,15 @@ impl CmdExecutor for ServerOpts {
app_config::AppConfig::new(&self.config_path).expect("Failed to load app config"); app_config::AppConfig::new(&self.config_path).expect("Failed to load app config");
let log: rtss_log::Logging = app_config.log.into(); let log: rtss_log::Logging = app_config.log.into();
log.init(); log.init();
rtss_api::serve(rtss_api::ServerConfig::new( rtss_api::serve(
&app_config.database.url, rtss_api::ServerConfig::new(&app_config.database.url, app_config.server.port)
app_config.server.port, .with_user_auth_client(rtss_api::UserAuthClient {
)) base_url: app_config.sso.base_url,
login_url: app_config.sso.login_url,
logout_url: app_config.sso.logout_url,
user_info_url: app_config.sso.user_info_url,
}),
)
.await .await
} }
} }