- apps/web: test updates (Vitest/setup), playbackAnalyticsService, TrackGrid, serviceErrorHandler - veza-common: logging, metrics, traits, validation, random - veza-stream-server: audio pipeline, codecs, cache, monitoring, routes - apps/web/dist_verification: refresh build assets (content-hashed filenames) Co-authored-by: Cursor <cursoragent@cursor.com>
379 lines
12 KiB
Rust
379 lines
12 KiB
Rust
//! Common traits for Veza Rust services
|
|
//!
|
|
//! This module defines shared traits that can be implemented by different
|
|
//! services to provide a consistent interface and enable easy testing.
|
|
|
|
use async_trait::async_trait;
|
|
use std::collections::HashMap;
|
|
use uuid::Uuid;
|
|
|
|
use crate::VezaResult;
|
|
|
|
/// Authentication provider trait
|
|
#[async_trait]
|
|
pub trait AuthProvider: Send + Sync {
|
|
/// Validate a JWT token
|
|
async fn validate_token(&self, token: &str) -> VezaResult<AuthClaims>;
|
|
|
|
/// Generate a new JWT token
|
|
async fn generate_token(&self, claims: &AuthClaims) -> VezaResult<String>;
|
|
|
|
/// Refresh a token
|
|
async fn refresh_token(&self, refresh_token: &str) -> VezaResult<AuthResult>;
|
|
|
|
/// Revoke a token
|
|
async fn revoke_token(&self, token: &str) -> VezaResult<()>;
|
|
|
|
/// Check if a user has permission for a resource
|
|
async fn check_permission(&self, user_id: Uuid, resource: &str, action: &str) -> VezaResult<bool>;
|
|
}
|
|
|
|
/// Message store trait for chat functionality
|
|
#[async_trait]
|
|
pub trait MessageStore: Send + Sync {
|
|
/// Store a message
|
|
async fn store_message(&self, message: &Message) -> VezaResult<()>;
|
|
|
|
/// Get messages for a conversation
|
|
async fn get_messages(&self, conversation_id: Uuid, limit: u32, offset: u32) -> VezaResult<Vec<Message>>;
|
|
|
|
/// Get a specific message
|
|
async fn get_message(&self, message_id: Uuid) -> VezaResult<Option<Message>>;
|
|
|
|
/// Update a message
|
|
async fn update_message(&self, message_id: Uuid, content: &str) -> VezaResult<()>;
|
|
|
|
/// Delete a message
|
|
async fn delete_message(&self, message_id: Uuid) -> VezaResult<()>;
|
|
|
|
/// Mark messages as read
|
|
async fn mark_as_read(&self, user_id: Uuid, conversation_id: Uuid, message_id: Uuid) -> VezaResult<()>;
|
|
|
|
/// Get unread count for a user
|
|
async fn get_unread_count(&self, user_id: Uuid) -> VezaResult<u32>;
|
|
}
|
|
|
|
/// Stream provider trait for audio streaming
|
|
#[async_trait]
|
|
pub trait StreamProvider: Send + Sync {
|
|
/// Get stream URL for a track
|
|
async fn get_stream_url(&self, track_id: Uuid, user_id: Uuid) -> VezaResult<String>;
|
|
|
|
/// Validate stream access
|
|
async fn validate_access(&self, track_id: Uuid, user_id: Uuid) -> VezaResult<bool>;
|
|
|
|
/// Get track metadata
|
|
async fn get_track_metadata(&self, track_id: Uuid) -> VezaResult<TrackMetadata>;
|
|
|
|
/// Get audio chunk
|
|
async fn get_audio_chunk(&self, track_id: Uuid, chunk_index: u32) -> VezaResult<Vec<u8>>;
|
|
|
|
/// Get playlist
|
|
async fn get_playlist(&self, playlist_id: Uuid, user_id: Uuid) -> VezaResult<Playlist>;
|
|
|
|
/// Create playlist
|
|
async fn create_playlist(&self, user_id: Uuid, name: &str, tracks: Vec<Uuid>) -> VezaResult<Playlist>;
|
|
}
|
|
|
|
/// Cache provider trait
|
|
#[async_trait]
|
|
pub trait CacheProvider: Send + Sync {
|
|
/// Get a value from cache
|
|
async fn get<T>(&self, key: &str) -> VezaResult<Option<T>>
|
|
where
|
|
T: serde::de::DeserializeOwned;
|
|
|
|
/// Set a value in cache
|
|
async fn set<T>(&self, key: &str, value: &T, ttl: Option<u64>) -> VezaResult<()>
|
|
where
|
|
T: serde::Serialize;
|
|
|
|
/// Delete a value from cache
|
|
async fn delete(&self, key: &str) -> VezaResult<()>;
|
|
|
|
/// Check if a key exists
|
|
async fn exists(&self, key: &str) -> VezaResult<bool>;
|
|
|
|
/// Get multiple values
|
|
async fn get_many<T>(&self, keys: &[String]) -> VezaResult<HashMap<String, T>>
|
|
where
|
|
T: serde::de::DeserializeOwned;
|
|
|
|
/// Set multiple values
|
|
async fn set_many<T>(&self, values: HashMap<String, T>, ttl: Option<u64>) -> VezaResult<()>
|
|
where
|
|
T: serde::Serialize;
|
|
|
|
/// Clear cache
|
|
async fn clear(&self) -> VezaResult<()>;
|
|
|
|
/// Get cache statistics
|
|
async fn stats(&self) -> VezaResult<CacheStats>;
|
|
}
|
|
|
|
/// Database provider trait
|
|
#[async_trait]
|
|
pub trait DatabaseProvider: Send + Sync {
|
|
/// Execute a query
|
|
async fn execute(&self, query: &str, params: &[serde_json::Value]) -> VezaResult<u64>;
|
|
|
|
/// Query a single row
|
|
async fn query_one<T>(&self, query: &str, params: &[serde_json::Value]) -> VezaResult<Option<T>>
|
|
where
|
|
T: serde::de::DeserializeOwned;
|
|
|
|
/// Query multiple rows
|
|
async fn query_many<T>(&self, query: &str, params: &[serde_json::Value]) -> VezaResult<Vec<T>>
|
|
where
|
|
T: serde::de::DeserializeOwned;
|
|
|
|
/// Begin a transaction
|
|
/// Note: This returns a type-erased transaction provider
|
|
/// Implementations should return their concrete transaction type
|
|
async fn begin_transaction(&self) -> VezaResult<()>;
|
|
|
|
/// Health check
|
|
async fn health_check(&self) -> VezaResult<()>;
|
|
}
|
|
|
|
/// Transaction provider trait
|
|
#[async_trait]
|
|
pub trait TransactionProvider: Send + Sync {
|
|
/// Execute a query in transaction
|
|
async fn execute(&self, query: &str, params: &[serde_json::Value]) -> VezaResult<u64>;
|
|
|
|
/// Query a single row in transaction
|
|
async fn query_one<T>(&self, query: &str, params: &[serde_json::Value]) -> VezaResult<Option<T>>
|
|
where
|
|
T: serde::de::DeserializeOwned;
|
|
|
|
/// Query multiple rows in transaction
|
|
async fn query_many<T>(&self, query: &str, params: &[serde_json::Value]) -> VezaResult<Vec<T>>
|
|
where
|
|
T: serde::de::DeserializeOwned;
|
|
|
|
/// Commit transaction
|
|
async fn commit(self: Box<Self>) -> VezaResult<()>;
|
|
|
|
/// Rollback transaction
|
|
async fn rollback(self: Box<Self>) -> VezaResult<()>;
|
|
}
|
|
|
|
/// Rate limiter trait
|
|
#[async_trait]
|
|
pub trait RateLimiter: Send + Sync {
|
|
/// Check if a request is allowed
|
|
async fn is_allowed(&self, key: &str, limit: u32, window: u64) -> VezaResult<bool>;
|
|
|
|
/// Get remaining requests
|
|
async fn get_remaining(&self, key: &str, limit: u32, window: u64) -> VezaResult<u32>;
|
|
|
|
/// Reset rate limit for a key
|
|
async fn reset(&self, key: &str) -> VezaResult<()>;
|
|
|
|
/// Get rate limit info
|
|
async fn get_info(&self, key: &str, limit: u32, window: u64) -> VezaResult<RateLimitInfo>;
|
|
}
|
|
|
|
/// Metrics provider trait
|
|
#[async_trait]
|
|
pub trait MetricsProvider: Send + Sync {
|
|
/// Increment a counter
|
|
async fn increment_counter(&self, name: &str, labels: &[(&str, &str)]) -> VezaResult<()>;
|
|
|
|
/// Increment a counter by a value
|
|
async fn increment_counter_by(&self, name: &str, value: f64, labels: &[(&str, &str)]) -> VezaResult<()>;
|
|
|
|
/// Set a gauge value
|
|
async fn set_gauge(&self, name: &str, value: f64, labels: &[(&str, &str)]) -> VezaResult<()>;
|
|
|
|
/// Observe a histogram value
|
|
async fn observe_histogram(&self, name: &str, value: f64, labels: &[(&str, &str)]) -> VezaResult<()>;
|
|
|
|
/// Observe a summary value
|
|
async fn observe_summary(&self, name: &str, value: f64, labels: &[(&str, &str)]) -> VezaResult<()>;
|
|
|
|
/// Get metrics as string
|
|
async fn get_metrics(&self) -> VezaResult<String>;
|
|
}
|
|
|
|
/// Logger trait
|
|
pub trait Logger: Send + Sync {
|
|
/// Log a trace message
|
|
fn trace(&self, message: &str, fields: &[(&str, String)]);
|
|
|
|
/// Log a debug message
|
|
fn debug(&self, message: &str, fields: &[(&str, String)]);
|
|
|
|
/// Log an info message
|
|
fn info(&self, message: &str, fields: &[(&str, String)]);
|
|
|
|
/// Log a warning message
|
|
fn warn(&self, message: &str, fields: &[(&str, String)]);
|
|
|
|
/// Log an error message
|
|
fn error(&self, message: &str, fields: &[(&str, String)]);
|
|
|
|
/// Create a child logger with additional fields
|
|
fn child(&self, fields: &[(&str, String)]) -> Box<dyn Logger>;
|
|
}
|
|
|
|
/// WebSocket handler trait
|
|
#[async_trait]
|
|
pub trait WebSocketHandler: Send + Sync {
|
|
/// Handle a new connection
|
|
async fn handle_connect(&self, user_id: Uuid, connection_id: Uuid) -> VezaResult<()>;
|
|
|
|
/// Handle a message
|
|
async fn handle_message(&self, user_id: Uuid, connection_id: Uuid, message: &str) -> VezaResult<()>;
|
|
|
|
/// Handle a disconnection
|
|
async fn handle_disconnect(&self, user_id: Uuid, connection_id: Uuid) -> VezaResult<()>;
|
|
|
|
/// Broadcast a message to all users in a conversation
|
|
async fn broadcast_to_conversation(&self, conversation_id: Uuid, message: &str, exclude_user: Option<Uuid>) -> VezaResult<()>;
|
|
|
|
/// Send a message to a specific user
|
|
async fn send_to_user(&self, user_id: Uuid, message: &str) -> VezaResult<()>;
|
|
}
|
|
|
|
/// File storage trait
|
|
#[async_trait]
|
|
pub trait FileStorage: Send + Sync {
|
|
/// Store a file
|
|
async fn store_file(&self, path: &str, content: &[u8]) -> VezaResult<String>;
|
|
|
|
/// Get a file
|
|
async fn get_file(&self, path: &str) -> VezaResult<Vec<u8>>;
|
|
|
|
/// Delete a file
|
|
async fn delete_file(&self, path: &str) -> VezaResult<()>;
|
|
|
|
/// Check if a file exists
|
|
async fn file_exists(&self, path: &str) -> VezaResult<bool>;
|
|
|
|
/// Get file metadata
|
|
async fn get_file_metadata(&self, path: &str) -> VezaResult<FileMetadata>;
|
|
|
|
/// List files in a directory
|
|
async fn list_files(&self, path: &str) -> VezaResult<Vec<String>>;
|
|
}
|
|
|
|
/// Notification service trait
|
|
#[async_trait]
|
|
pub trait NotificationService: Send + Sync {
|
|
/// Send a push notification
|
|
async fn send_push_notification(&self, user_id: Uuid, title: &str, body: &str, data: &HashMap<String, String>) -> VezaResult<()>;
|
|
|
|
/// Send an in-app notification
|
|
async fn send_in_app_notification(&self, user_id: Uuid, message: &str, data: &HashMap<String, serde_json::Value>) -> VezaResult<()>;
|
|
|
|
/// Send an email
|
|
async fn send_email(&self, to: &str, subject: &str, body: &str) -> VezaResult<()>;
|
|
|
|
/// Send an SMS
|
|
async fn send_sms(&self, to: &str, message: &str) -> VezaResult<()>;
|
|
}
|
|
|
|
/// Health check trait
|
|
#[async_trait]
|
|
pub trait HealthCheck: Send + Sync {
|
|
/// Check if the service is healthy
|
|
async fn is_healthy(&self) -> VezaResult<bool>;
|
|
|
|
/// Get detailed health information
|
|
async fn get_health_info(&self) -> VezaResult<HealthInfo>;
|
|
|
|
/// Get service name
|
|
fn service_name(&self) -> &str;
|
|
|
|
/// Get service version
|
|
fn service_version(&self) -> &str;
|
|
}
|
|
|
|
// Data types used by traits
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct AuthClaims {
|
|
pub user_id: Uuid,
|
|
pub username: String,
|
|
pub email: String,
|
|
pub roles: Vec<String>,
|
|
pub exp: u64,
|
|
pub iat: u64,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct AuthResult {
|
|
pub access_token: String,
|
|
pub refresh_token: String,
|
|
pub expires_in: u64,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct Message {
|
|
pub id: Uuid,
|
|
pub conversation_id: Uuid,
|
|
pub user_id: Uuid,
|
|
pub content: String,
|
|
pub message_type: String,
|
|
pub created_at: chrono::DateTime<chrono::Utc>,
|
|
pub updated_at: chrono::DateTime<chrono::Utc>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct TrackMetadata {
|
|
pub id: Uuid,
|
|
pub title: String,
|
|
pub artist: String,
|
|
pub album: String,
|
|
pub duration: u32,
|
|
pub file_size: u64,
|
|
pub format: String,
|
|
pub bitrate: u32,
|
|
pub sample_rate: u32,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct Playlist {
|
|
pub id: Uuid,
|
|
pub name: String,
|
|
pub user_id: Uuid,
|
|
pub tracks: Vec<Uuid>,
|
|
pub created_at: chrono::DateTime<chrono::Utc>,
|
|
pub updated_at: chrono::DateTime<chrono::Utc>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct CacheStats {
|
|
pub hits: u64,
|
|
pub misses: u64,
|
|
pub keys: u64,
|
|
pub memory_usage: u64,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct RateLimitInfo {
|
|
pub limit: u32,
|
|
pub remaining: u32,
|
|
pub reset_time: u64,
|
|
pub retry_after: Option<u64>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct FileMetadata {
|
|
pub path: String,
|
|
pub size: u64,
|
|
pub mime_type: String,
|
|
pub created_at: chrono::DateTime<chrono::Utc>,
|
|
pub modified_at: chrono::DateTime<chrono::Utc>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct HealthInfo {
|
|
pub status: String,
|
|
pub version: String,
|
|
pub uptime: u64,
|
|
pub dependencies: HashMap<String, String>,
|
|
pub metrics: HashMap<String, serde_json::Value>,
|
|
}
|