nap_core/provider/
remote.rs1use anyhow::{Context, Result};
8use tracing::info;
9
10use super::{Provider, ProviderStatus, ProviderType};
11
12pub struct RemoteProvider {
14 url_base: String,
15 workspace_id: String,
16 auth_token: Option<String>,
17}
18
19impl RemoteProvider {
20 pub fn new(url_base: &str, workspace_id: &str) -> Self {
22 Self {
23 url_base: url_base.to_string(),
24 workspace_id: workspace_id.to_string(),
25 auth_token: std::env::var("NAP_REMOTE_AUTH_TOKEN").ok(),
26 }
27 }
28
29 pub fn new_with_default_workspace(url_base: &str) -> Self {
31 Self {
32 url_base: url_base.to_string(),
33 workspace_id: super::get_default_workspace_id(),
34 auth_token: std::env::var("NAP_REMOTE_AUTH_TOKEN").ok(),
35 }
36 }
37
38 pub fn with_auth_token(mut self, token: &str) -> Self {
40 self.auth_token = Some(token.to_string());
41 self
42 }
43
44 fn http_health_url(&self) -> Result<String> {
46 let url = if self.url_base.starts_with("lore://") {
48 self.url_base.replace("lore://", "http://")
49 } else if self.url_base.starts_with("lores://") {
50 self.url_base.replace("lores://", "https://")
51 } else {
52 anyhow::bail!("Invalid Lore URL format: {}", self.url_base);
53 };
54
55 Ok(format!("{}/health_check", url))
56 }
57}
58
59#[async_trait::async_trait]
60impl Provider for RemoteProvider {
61 fn provider_type(&self) -> ProviderType {
62 ProviderType::Remote
63 }
64
65 fn name(&self) -> &str {
66 "Remote Lore Server"
67 }
68
69 async fn initialize(&self) -> Result<()> {
70 info!("Initializing Remote provider for {}", self.url_base);
71 info!("Remote provider initialized");
72 Ok(())
73 }
74
75 async fn ensure_ready(&self) -> Result<()> {
76 info!("Ensuring Remote provider is ready");
77
78 self.initialize().await?;
79
80 let health_url = self.http_health_url()?;
82 let response = reqwest::get(&health_url)
83 .await
84 .context("Failed to connect to remote Lore server")?;
85
86 if !response.status().is_success() {
87 anyhow::bail!(
88 "Remote Lore server health check failed: {}",
89 response.status()
90 );
91 }
92
93 info!("Remote provider is ready");
94 Ok(())
95 }
96
97 fn lore_url_base(&self) -> Result<String> {
98 Ok(self.url_base.clone())
99 }
100
101 fn workspace_id(&self) -> &str {
102 &self.workspace_id
103 }
104
105 async fn health_check(&self) -> Result<bool> {
106 let health_url = self.http_health_url()?;
107 match reqwest::get(&health_url).await {
108 Ok(response) => Ok(response.status().is_success()),
109 Err(_) => Ok(false),
110 }
111 }
112
113 async fn status(&self) -> Result<ProviderStatus> {
114 let healthy = self.health_check().await.unwrap_or(false);
115
116 let message = if healthy {
117 "Connected".to_string()
118 } else {
119 "Server unreachable".to_string()
120 };
121
122 Ok(ProviderStatus {
123 provider_type: self.provider_type(),
124 ready: healthy,
125 healthy,
126 url_base: self.url_base.clone(),
127 workspace_id: self.workspace_id.clone(),
128 message,
129 })
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn test_remote_provider_creation() {
139 let provider = RemoteProvider::new("lore://localhost:41337", "default");
140 assert_eq!(provider.provider_type(), ProviderType::Remote);
141 assert_eq!(provider.name(), "Remote Lore Server");
142 assert_eq!(provider.workspace_id(), "default");
143 assert_eq!(provider.url_base, "lore://localhost:41337");
144 }
145
146 #[test]
147 fn test_remote_provider_custom_auth() {
148 let provider = RemoteProvider::new("lore://localhost:41337", "default")
149 .with_auth_token("custom-token");
150 assert_eq!(provider.auth_token, Some("custom-token".to_string()));
151 }
152
153 #[test]
154 fn test_http_health_url() {
155 let provider = RemoteProvider::new("lore://localhost:41337", "default");
156 assert_eq!(
157 provider.http_health_url().unwrap(),
158 "http://localhost:41337/health_check"
159 );
160
161 let provider = RemoteProvider::new("lores://example.com:41337", "default");
162 assert_eq!(
163 provider.http_health_url().unwrap(),
164 "https://example.com:41337/health_check"
165 );
166 }
167}