use super::{api_session, ApiSession, NetworkSession};
use crate::error::{ZitiError, ZitiResult};
use crate::identity::IdentityManager;
use crate::transport::http::controller_client;
use serde::Deserialize;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock;
#[derive(Debug, Deserialize)]
struct CreateSessionResponse {
data: SessionData,
}
#[derive(Debug, Deserialize)]
struct SessionData {
id: String,
}
#[derive(Clone)]
pub struct SessionManager {
identity_manager: Arc<IdentityManager>,
current_api_session: Arc<RwLock<Option<ApiSession>>>,
}
impl SessionManager {
pub fn new(identity_manager: IdentityManager) -> Self {
Self {
identity_manager: Arc::new(identity_manager),
current_api_session: Arc::new(RwLock::new(None)),
}
}
pub async fn get_api_session(&self) -> ZitiResult<ApiSession> {
{
let session_guard = self.current_api_session.read().await;
if let Some(ref session) = *session_guard
&& !session.is_expired()
{
return Ok(session.clone());
}
}
let new_session = api_session::authenticate(&self.identity_manager).await?;
{
let mut session_guard = self.current_api_session.write().await;
*session_guard = Some(new_session.clone());
}
Ok(new_session)
}
pub async fn get_network_session(&self, service_id: &str) -> ZitiResult<NetworkSession> {
let api_session = self.get_api_session().await?;
let client = controller_client(&self.identity_manager, Duration::from_secs(30)).await?;
let sessions_url = format!(
"{}/sessions",
self.identity_manager.zt_api().trim_end_matches('/')
);
let request_body = serde_json::json!({
"serviceId": service_id,
"type": "Dial",
});
let response = client
.post(&sessions_url)
.header("Content-Type", "application/json")
.header("zt-session", &api_session.token)
.json(&request_body)
.send()
.await
.map_err(|e| {
ZitiError::ConnectionFailed(format!("Failed to create network session: {}", e))
})?;
if !response.status().is_success() {
let status = response.status();
let error_text = response
.text()
.await
.unwrap_or_else(|_| "Unknown error".to_string());
return Err(ZitiError::ProtocolError {
message: format!(
"Network session request failed with status {}: {}",
status, error_text
),
});
}
let session_response: CreateSessionResponse =
response.json().await.map_err(|e| ZitiError::ProtocolError {
message: format!("Failed to parse network session response: {}", e),
})?;
Ok(NetworkSession::new(
session_response.data.id,
service_id.to_string(),
))
}
pub async fn clear_api_session(&self) {
let mut session_guard = self.current_api_session.write().await;
*session_guard = None;
}
pub async fn has_valid_api_session(&self) -> bool {
let session_guard = self.current_api_session.read().await;
if let Some(ref session) = *session_guard {
!session.is_expired()
} else {
false
}
}
pub fn identity_manager(&self) -> &IdentityManager {
&self.identity_manager
}
}
#[cfg(test)]
mod tests {
}