Skip to main content

lore_cli/cloud/
mod.rs

1//! Cloud sync module for Lore.
2//!
3//! Provides functionality for syncing sessions to the Lore cloud service,
4//! including authentication, encryption, and API communication.
5//!
6//! # Submodules
7//!
8//! - `client` - HTTP client for cloud API communication
9//! - `credentials` - Secure credential storage (keychain + fallback)
10//! - `encryption` - End-to-end encryption for session content
11
12pub mod client;
13pub mod credentials;
14pub mod encryption;
15
16// Re-exports for external use
17#[allow(unused_imports)]
18pub use client::CloudClient;
19#[allow(unused_imports)]
20pub use credentials::{Credentials, CredentialsStore};
21#[allow(unused_imports)]
22pub use encryption::{decrypt_data, derive_key, encrypt_data};
23
24/// Default cloud service URL.
25pub const DEFAULT_CLOUD_URL: &str = "https://app.lore.varalys.com";
26
27/// Service name for keyring storage.
28pub const KEYRING_SERVICE: &str = "lore-cloud";
29
30/// User identifier for API key in keyring.
31pub const KEYRING_API_KEY_USER: &str = "api-key";
32
33/// User identifier for encryption key in keyring.
34pub const KEYRING_ENCRYPTION_KEY_USER: &str = "encryption-key";
35
36/// Custom error type for cloud operations.
37#[derive(Debug, thiserror::Error)]
38pub enum CloudError {
39    /// Not logged in to the cloud service.
40    #[error("Not logged in. Run 'lore login' first.")]
41    NotLoggedIn,
42
43    /// Authentication failed.
44    #[error("Authentication failed: {0}")]
45    #[allow(dead_code)]
46    AuthFailed(String),
47
48    /// Network or API error.
49    #[error("Cloud API error: {0}")]
50    #[allow(dead_code)]
51    ApiError(String),
52
53    /// HTTP request error.
54    #[error("HTTP request failed: {0}")]
55    HttpError(#[from] reqwest::Error),
56
57    /// Encryption or decryption error.
58    #[error("Encryption error: {0}")]
59    EncryptionError(String),
60
61    /// Keyring storage error.
62    #[error("Credential storage error: {0}")]
63    KeyringError(String),
64
65    /// Invalid or missing encryption key.
66    #[error("Encryption key not set. Run 'lore cloud push' to set up encryption.")]
67    #[allow(dead_code)]
68    NoEncryptionKey,
69
70    /// State mismatch during OAuth callback.
71    #[error("OAuth state mismatch - possible CSRF attack")]
72    #[allow(dead_code)]
73    StateMismatch,
74
75    /// Login timeout.
76    #[error("Login timed out waiting for browser authentication")]
77    #[allow(dead_code)]
78    LoginTimeout,
79
80    /// Server returned an error response.
81    #[error("Server error ({status}): {message}")]
82    ServerError { status: u16, message: String },
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_cloud_error_display_not_logged_in() {
91        let err = CloudError::NotLoggedIn;
92        assert!(err.to_string().contains("Not logged in"));
93    }
94
95    #[test]
96    fn test_cloud_error_display_auth_failed() {
97        let err = CloudError::AuthFailed("invalid token".to_string());
98        assert!(err.to_string().contains("invalid token"));
99    }
100
101    #[test]
102    fn test_cloud_error_display_server_error() {
103        let err = CloudError::ServerError {
104            status: 500,
105            message: "Internal error".to_string(),
106        };
107        assert!(err.to_string().contains("500"));
108        assert!(err.to_string().contains("Internal error"));
109    }
110
111    #[test]
112    fn test_default_cloud_url() {
113        assert_eq!(DEFAULT_CLOUD_URL, "https://app.lore.varalys.com");
114    }
115
116    #[test]
117    fn test_keyring_constants() {
118        assert_eq!(KEYRING_SERVICE, "lore-cloud");
119        assert_eq!(KEYRING_API_KEY_USER, "api-key");
120        assert_eq!(KEYRING_ENCRYPTION_KEY_USER, "encryption-key");
121    }
122}