livekit wip
This commit is contained in:
		
							
								
								
									
										1245
									
								
								examples/meet/server/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1245
									
								
								examples/meet/server/Cargo.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										17
									
								
								examples/meet/server/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								examples/meet/server/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "meet-server"
 | 
			
		||||
version = "0.1.0"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
 | 
			
		||||
[workspace]
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
tokio = { version = "1.0", features = ["full"] }
 | 
			
		||||
axum = "0.7"
 | 
			
		||||
tower = "0.4"
 | 
			
		||||
tower-http = { version = "0.5", features = ["cors", "fs"] }
 | 
			
		||||
serde = { version = "1.0", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0"
 | 
			
		||||
uuid = { version = "1.0", features = ["v4"] }
 | 
			
		||||
jsonwebtoken = "9.0"
 | 
			
		||||
chrono = { version = "0.4", features = ["serde"] }
 | 
			
		||||
							
								
								
									
										63
									
								
								examples/meet/server/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								examples/meet/server/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
# LiveKit Meet Server
 | 
			
		||||
 | 
			
		||||
A simple backend server to provide connection details for the LiveKit Meet demo app.
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
 | 
			
		||||
- `/api/connection-details` endpoint for room connection details
 | 
			
		||||
- CORS support for frontend requests
 | 
			
		||||
- Static file serving for the built WASM app
 | 
			
		||||
- Health check endpoint
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
1. Build the frontend first:
 | 
			
		||||
```bash
 | 
			
		||||
cd .. && trunk build
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
2. Run the server:
 | 
			
		||||
```bash
 | 
			
		||||
cd server && cargo run
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
3. Access the app at: http://localhost:8083
 | 
			
		||||
 | 
			
		||||
## API Endpoints
 | 
			
		||||
 | 
			
		||||
- `GET /api/connection-details?roomName=<room>&participantName=<name>` - Get connection details
 | 
			
		||||
- `GET /health` - Health check
 | 
			
		||||
 | 
			
		||||
## Production Setup
 | 
			
		||||
 | 
			
		||||
For production use, you'll need to:
 | 
			
		||||
 | 
			
		||||
1. Replace the mock token generation with proper LiveKit JWT tokens
 | 
			
		||||
2. Add your actual LiveKit server URL
 | 
			
		||||
3. Add proper authentication and validation
 | 
			
		||||
4. Use environment variables for configuration
 | 
			
		||||
 | 
			
		||||
Example with real LiveKit tokens:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use livekit_api::access_token::{AccessToken, VideoGrant};
 | 
			
		||||
 | 
			
		||||
fn generate_token(room_name: &str, participant_name: &str) -> String {
 | 
			
		||||
    let api_key = std::env::var("LIVEKIT_API_KEY").unwrap();
 | 
			
		||||
    let api_secret = std::env::var("LIVEKIT_API_SECRET").unwrap();
 | 
			
		||||
    
 | 
			
		||||
    let grant = VideoGrant {
 | 
			
		||||
        room_join: true,
 | 
			
		||||
        room: room_name.to_string(),
 | 
			
		||||
        ..Default::default()
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    let token = AccessToken::new(&api_key, &api_secret)
 | 
			
		||||
        .with_identity(participant_name)
 | 
			
		||||
        .with_video_grant(grant)
 | 
			
		||||
        .to_jwt()
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        
 | 
			
		||||
    token
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										139
									
								
								examples/meet/server/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								examples/meet/server/src/main.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,139 @@
 | 
			
		||||
use axum::{
 | 
			
		||||
    extract::Query,
 | 
			
		||||
    http::StatusCode,
 | 
			
		||||
    response::Json,
 | 
			
		||||
    routing::get,
 | 
			
		||||
    Router,
 | 
			
		||||
};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
use tower_http::cors::CorsLayer;
 | 
			
		||||
use tower_http::services::ServeDir;
 | 
			
		||||
use jsonwebtoken::{encode, Header, EncodingKey, Algorithm};
 | 
			
		||||
use chrono::{Utc, Duration};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Deserialize)]
 | 
			
		||||
struct ConnectionRequest {
 | 
			
		||||
    #[serde(rename = "roomName")]
 | 
			
		||||
    room_name: String,
 | 
			
		||||
    #[serde(rename = "participantName")]
 | 
			
		||||
    participant_name: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize)]
 | 
			
		||||
struct ConnectionDetails {
 | 
			
		||||
    server_url: String,
 | 
			
		||||
    participant_token: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn connection_details(
 | 
			
		||||
    Query(params): Query<ConnectionRequest>,
 | 
			
		||||
) -> Result<Json<ConnectionDetails>, StatusCode> {
 | 
			
		||||
    println!("Connection request: room={}, participant={}", 
 | 
			
		||||
             params.room_name, params.participant_name);
 | 
			
		||||
 | 
			
		||||
    // For demo purposes, use a mock LiveKit server URL
 | 
			
		||||
    // In production, you would:
 | 
			
		||||
    // 1. Validate the room and participant
 | 
			
		||||
    // 2. Generate a proper JWT token with LiveKit API key/secret
 | 
			
		||||
    // 3. Return your actual LiveKit server URL
 | 
			
		||||
    
 | 
			
		||||
    let token = match generate_livekit_token(¶ms.room_name, ¶ms.participant_name) {
 | 
			
		||||
        Ok(token) => token,
 | 
			
		||||
        Err(e) => {
 | 
			
		||||
            println!("Failed to generate token: {}", e);
 | 
			
		||||
            return Err(StatusCode::INTERNAL_SERVER_ERROR);
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    let details = ConnectionDetails {
 | 
			
		||||
        server_url: std::env::var("LIVEKIT_URL")
 | 
			
		||||
            .unwrap_or_else(|_| "wss://meet-demo.livekit.cloud".to_string()),
 | 
			
		||||
        participant_token: token,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    Ok(Json(details))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize)]
 | 
			
		||||
struct LiveKitClaims {
 | 
			
		||||
    iss: String,
 | 
			
		||||
    sub: String,
 | 
			
		||||
    iat: i64,
 | 
			
		||||
    exp: i64,
 | 
			
		||||
    video: VideoGrant,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Serialize)]
 | 
			
		||||
struct VideoGrant {
 | 
			
		||||
    #[serde(rename = "roomJoin")]
 | 
			
		||||
    room_join: bool,
 | 
			
		||||
    room: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn generate_livekit_token(room_name: &str, participant_name: &str) -> Result<String, String> {
 | 
			
		||||
    // For demo purposes - you should set these as environment variables
 | 
			
		||||
    let api_key = std::env::var("LIVEKIT_API_KEY")
 | 
			
		||||
        .unwrap_or_else(|_| "devkey".to_string());
 | 
			
		||||
    let api_secret = std::env::var("LIVEKIT_API_SECRET")
 | 
			
		||||
        .unwrap_or_else(|_| "secret".to_string());
 | 
			
		||||
    
 | 
			
		||||
    println!("Generating token with:");
 | 
			
		||||
    println!("  API Key: {}", api_key);
 | 
			
		||||
    println!("  API Secret: {} (length: {})", 
 | 
			
		||||
             if api_secret.len() > 10 { format!("{}...", &api_secret[..10]) } else { api_secret.clone() },
 | 
			
		||||
             api_secret.len());
 | 
			
		||||
    println!("  Room: {}", room_name);
 | 
			
		||||
    println!("  Participant: {}", participant_name);
 | 
			
		||||
    
 | 
			
		||||
    let now = Utc::now();
 | 
			
		||||
    let exp = now + Duration::hours(6); // Token valid for 6 hours
 | 
			
		||||
    
 | 
			
		||||
    let claims = LiveKitClaims {
 | 
			
		||||
        iss: api_key.clone(),
 | 
			
		||||
        sub: participant_name.to_string(),
 | 
			
		||||
        iat: now.timestamp(),
 | 
			
		||||
        exp: exp.timestamp(),
 | 
			
		||||
        video: VideoGrant {
 | 
			
		||||
            room_join: true,
 | 
			
		||||
            room: room_name.to_string(),
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
    
 | 
			
		||||
    let header = Header::new(Algorithm::HS256);
 | 
			
		||||
    let encoding_key = EncodingKey::from_secret(api_secret.as_ref());
 | 
			
		||||
    
 | 
			
		||||
    encode(&header, &claims, &encoding_key)
 | 
			
		||||
        .map_err(|e| format!("Failed to generate token: {}", e))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn health() -> &'static str {
 | 
			
		||||
    "OK"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::main]
 | 
			
		||||
async fn main() {
 | 
			
		||||
    println!("Starting LiveKit Meet server on http://localhost:8083");
 | 
			
		||||
    
 | 
			
		||||
    // Log environment variables for debugging
 | 
			
		||||
    println!("Environment variables:");
 | 
			
		||||
    println!("  LIVEKIT_URL: {:?}", std::env::var("LIVEKIT_URL"));
 | 
			
		||||
    println!("  LIVEKIT_API_KEY: {:?}", std::env::var("LIVEKIT_API_KEY"));
 | 
			
		||||
    println!("  LIVEKIT_API_SECRET: {:?}", std::env::var("LIVEKIT_API_SECRET"));
 | 
			
		||||
 | 
			
		||||
    let app = Router::new()
 | 
			
		||||
        .route("/api/connection-details", get(connection_details))
 | 
			
		||||
        .route("/health", get(health))
 | 
			
		||||
        // Serve static files from the dist directory
 | 
			
		||||
        .nest_service("/", ServeDir::new("../dist"))
 | 
			
		||||
        .layer(CorsLayer::permissive());
 | 
			
		||||
 | 
			
		||||
    let listener = tokio::net::TcpListener::bind("0.0.0.0:8083")
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
        
 | 
			
		||||
    println!("Server running on http://localhost:8083");
 | 
			
		||||
    println!("API endpoint: http://localhost:8083/api/connection-details");
 | 
			
		||||
    
 | 
			
		||||
    axum::serve(listener, app).await.unwrap();
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user