Skip to main content

vectorizer_sdk/client/
core.rs

1//! Server-status + session surface: `health_check`, `login`.
2//!
3//! Lives in its own module because it doesn't fit any of the
4//! domain-specific surfaces (collections / vectors / search / ...)
5//! and likely grows to include `/metrics`, `/stats`, and similar
6//! observability endpoints in future releases.
7
8use serde::Deserialize;
9
10use super::VectorizerClient;
11use crate::error::{Result, VectorizerError};
12use crate::models::*;
13
14/// Subset of `POST /auth/login`'s response shape — we only need the
15/// JWT access token for subsequent requests, the full user record +
16/// expiry are ignored here and can be recovered by the caller via
17/// the standard `/auth/me` endpoint if needed.
18#[derive(Debug, Deserialize)]
19struct LoginResponse {
20    access_token: String,
21}
22
23/// JWT minted by `/auth/login`. Returned by [`VectorizerClient::login`]
24/// so callers can hand it back into a new [`VectorizerClient`] via
25/// `ClientConfig::api_key` — `HttpTransport::new` sniffs the shape and
26/// sends it as `Authorization: Bearer <token>`.
27#[derive(Debug, Clone)]
28pub struct JwtToken {
29    /// Raw encoded JWT (three base64url segments separated by `.`).
30    pub access_token: String,
31}
32
33impl VectorizerClient {
34    /// Check server health.
35    pub async fn health_check(&self) -> Result<HealthStatus> {
36        let response = self.make_request("GET", "/health", None).await?;
37        let health: HealthStatus = serde_json::from_str(&response).map_err(|e| {
38            VectorizerError::server(format!("Failed to parse health check response: {e}"))
39        })?;
40        Ok(health)
41    }
42
43    /// Exchange a `(username, password)` pair for a JWT via
44    /// `POST /auth/login`. The returned token is **not** retained by
45    /// `self` — the transport was built with whatever credential
46    /// (or none) was passed at construction. To use the JWT for
47    /// subsequent requests, construct a new client with
48    /// `ClientConfig::api_key = Some(jwt.access_token)`; the
49    /// HTTP transport recognises the three-segment JWT shape and
50    /// sends it as `Authorization: Bearer …` rather than
51    /// `X-API-Key`.
52    ///
53    /// When the server runs with `auth.enabled: false` this endpoint
54    /// may 404 — callers running against a no-auth dev server don't
55    /// need to call `login` at all.
56    pub async fn login(&self, username: &str, password: &str) -> Result<JwtToken> {
57        let payload = serde_json::json!({
58            "username": username,
59            "password": password,
60        });
61        let response = self
62            .make_request("POST", "/auth/login", Some(payload))
63            .await?;
64        let parsed: LoginResponse = serde_json::from_str(&response)
65            .map_err(|e| VectorizerError::server(format!("Failed to parse login response: {e}")))?;
66        Ok(JwtToken {
67            access_token: parsed.access_token,
68        })
69    }
70}