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