- libs/rpc/proto/: admin.proto with 8 RPC methods - libs/rpc/admin/: tonic server impl (SessionAdminService), client wrapper (AdminGrpcClient), types, generated/ tonic-prost build output - libs/rpc/build.rs: tonic-prost-build two-step (proto -> message types + manual service defs) - libs/rpc/lib.rs: module re-exports - libs/session_manager/: session manager types used by admin service
169 lines
6.2 KiB
Rust
169 lines
6.2 KiB
Rust
//! Tonic gRPC client wrapper for SessionAdmin service.
|
|
|
|
use session_manager::{OnlineStatus, SessionInfo, UserSession};
|
|
use tonic::transport::Channel;
|
|
use uuid::Uuid;
|
|
|
|
use super::generated::admin::{
|
|
ListWorkspaceSessionsRequest, ListUserSessionsRequest,
|
|
KickUserFromWorkspaceRequest, KickUserRequest,
|
|
GetUserStatusRequest, GetUserInfoRequest,
|
|
GetWorkspaceOnlineUsersRequest, IsUserOnlineRequest,
|
|
};
|
|
use super::generated::admin_session_admin::session_admin_client::SessionAdminClient;
|
|
use super::types::from_proto_status;
|
|
|
|
/// Auto-generated gRPC client type.
|
|
pub type GrpcClient = SessionAdminClient<Channel>;
|
|
|
|
/// Thin wrapper around the generated SessionAdminClient.
|
|
pub struct AdminGrpcClient {
|
|
inner: GrpcClient,
|
|
}
|
|
|
|
impl AdminGrpcClient {
|
|
/// Connect to the gRPC server at the given URI.
|
|
pub async fn connect(uri: tonic::codegen::http::Uri) -> anyhow::Result<Self> {
|
|
let inner = SessionAdminClient::connect(uri).await?;
|
|
Ok(Self { inner })
|
|
}
|
|
|
|
/// Wrap an existing channel.
|
|
pub fn new(inner: GrpcClient) -> Self {
|
|
Self { inner }
|
|
}
|
|
|
|
pub async fn list_workspace_sessions(
|
|
&mut self,
|
|
workspace_id: Uuid,
|
|
) -> anyhow::Result<Vec<UserSession>> {
|
|
let req = tonic::Request::new(ListWorkspaceSessionsRequest {
|
|
workspace_id: workspace_id.to_string(),
|
|
});
|
|
let res = self.inner.list_workspace_sessions(req).await
|
|
.map_err(|e| anyhow::anyhow!("gRPC error: {}", e))?;
|
|
Ok(res.into_inner().sessions
|
|
.into_iter()
|
|
.map(from_proto_session)
|
|
.collect())
|
|
}
|
|
|
|
pub async fn list_user_sessions(
|
|
&mut self,
|
|
user_id: Uuid,
|
|
) -> anyhow::Result<Vec<UserSession>> {
|
|
let req = tonic::Request::new(ListUserSessionsRequest {
|
|
user_id: user_id.to_string(),
|
|
});
|
|
let res = self.inner.list_user_sessions(req).await
|
|
.map_err(|e| anyhow::anyhow!("gRPC error: {}", e))?;
|
|
Ok(res.into_inner().sessions
|
|
.into_iter()
|
|
.map(from_proto_session)
|
|
.collect())
|
|
}
|
|
|
|
pub async fn kick_user_from_workspace(
|
|
&mut self,
|
|
user_id: Uuid,
|
|
workspace_id: Uuid,
|
|
) -> anyhow::Result<usize> {
|
|
let req = tonic::Request::new(KickUserFromWorkspaceRequest {
|
|
user_id: user_id.to_string(),
|
|
workspace_id: workspace_id.to_string(),
|
|
});
|
|
let res = self.inner.kick_user_from_workspace(req).await
|
|
.map_err(|e| anyhow::anyhow!("gRPC error: {}", e))?;
|
|
Ok(res.into_inner().kicked_count as usize)
|
|
}
|
|
|
|
pub async fn kick_user(&mut self, user_id: Uuid) -> anyhow::Result<usize> {
|
|
let req = tonic::Request::new(KickUserRequest {
|
|
user_id: user_id.to_string(),
|
|
});
|
|
let res = self.inner.kick_user(req).await
|
|
.map_err(|e| anyhow::anyhow!("gRPC error: {}", e))?;
|
|
Ok(res.into_inner().kicked_count as usize)
|
|
}
|
|
|
|
pub async fn get_user_status(&mut self, user_id: Uuid) -> anyhow::Result<OnlineStatus> {
|
|
let req = tonic::Request::new(GetUserStatusRequest {
|
|
user_id: user_id.to_string(),
|
|
});
|
|
let res = self.inner.get_user_status(req).await
|
|
.map_err(|e| anyhow::anyhow!("gRPC error: {}", e))?;
|
|
let status = super::generated::admin::OnlineStatus::try_from(res.get_ref().status)
|
|
.unwrap_or(super::generated::admin::OnlineStatus::Offline);
|
|
Ok(from_proto_status(status))
|
|
}
|
|
|
|
pub async fn get_user_info(
|
|
&mut self,
|
|
user_id: Uuid,
|
|
) -> anyhow::Result<Option<SessionInfo>> {
|
|
let req = tonic::Request::new(GetUserInfoRequest {
|
|
user_id: user_id.to_string(),
|
|
});
|
|
let res = self.inner.get_user_info(req).await
|
|
.map_err(|e| anyhow::anyhow!("gRPC error: {}", e))?;
|
|
Ok(res.into_inner().info.map(|i| from_proto_info(&i)))
|
|
}
|
|
|
|
pub async fn get_workspace_online_users(
|
|
&mut self,
|
|
workspace_id: Uuid,
|
|
) -> anyhow::Result<Vec<Uuid>> {
|
|
let req = tonic::Request::new(GetWorkspaceOnlineUsersRequest {
|
|
workspace_id: workspace_id.to_string(),
|
|
});
|
|
let res = self.inner.get_workspace_online_users(req).await
|
|
.map_err(|e| anyhow::anyhow!("gRPC error: {}", e))?;
|
|
res.into_inner().user_ids
|
|
.into_iter()
|
|
.map(|s| Uuid::parse_str(&s))
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|e| anyhow::anyhow!("invalid UUID in response: {}", e))
|
|
}
|
|
|
|
pub async fn is_user_online(&mut self, user_id: Uuid) -> anyhow::Result<bool> {
|
|
let req = tonic::Request::new(IsUserOnlineRequest {
|
|
user_id: user_id.to_string(),
|
|
});
|
|
let res = self.inner.is_user_online(req).await
|
|
.map_err(|e| anyhow::anyhow!("gRPC error: {}", e))?;
|
|
Ok(res.into_inner().online)
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Proto → session_manager type conversions
|
|
// ---------------------------------------------------------------------------
|
|
|
|
fn from_proto_session(s: super::generated::admin::UserSession) -> UserSession {
|
|
UserSession {
|
|
session_id: Uuid::parse_str(&s.session_id).unwrap_or_default(),
|
|
user_id: Uuid::parse_str(&s.user_id).unwrap_or_default(),
|
|
workspace_id: Uuid::parse_str(&s.workspace_id).unwrap_or_default(),
|
|
ip_address: s.ip_address,
|
|
user_agent: s.user_agent,
|
|
connected_at: s.connected_at
|
|
.and_then(|ts| chrono::DateTime::from_timestamp(ts.seconds, ts.nanos as u32))
|
|
.unwrap_or_else(chrono::Utc::now),
|
|
last_heartbeat: s.last_heartbeat
|
|
.and_then(|ts| chrono::DateTime::from_timestamp(ts.seconds, ts.nanos as u32))
|
|
.unwrap_or_else(chrono::Utc::now),
|
|
}
|
|
}
|
|
|
|
fn from_proto_info(info: &super::generated::admin::SessionInfo) -> SessionInfo {
|
|
SessionInfo {
|
|
user_id: Uuid::parse_str(&info.user_id).unwrap_or_default(),
|
|
session_count: info.session_count as usize,
|
|
workspaces: info.workspaces
|
|
.iter()
|
|
.filter_map(|w| Uuid::parse_str(w).ok())
|
|
.collect(),
|
|
latest_session: info.latest_session.as_ref().map(|s| from_proto_session(s.clone())),
|
|
}
|
|
}
|