Update application servers to return json responses

This commit is contained in:
juancarmore 2024-05-24 12:37:40 +02:00
parent e24f9a6862
commit f0b108444c
15 changed files with 155 additions and 90 deletions

View File

@ -68,7 +68,8 @@ export class AppComponent implements OnDestroy {
// Publish your camera and microphone
await this.room.localParticipant.enableCameraAndMicrophone();
} catch (error: any) {
console.log('There was an error connecting to the room:', error?.message);
console.log('There was an error connecting to the room:', error?.error?.errorMessage || error?.message || error);
await this.leaveRoom();
}
}
@ -98,8 +99,9 @@ export class AppComponent implements OnDestroy {
* access to the endpoints.
*/
async getToken(roomName: string, participantName: string): Promise<string> {
return lastValueFrom(
this.httpClient.post<string>(APPLICATION_SERVER_URL + 'token', { roomName, participantName })
const response = await lastValueFrom(
this.httpClient.post<{ token: string }>(APPLICATION_SERVER_URL + 'token', { roomName, participantName })
);
return response.token;
}
}

View File

@ -69,6 +69,7 @@ async function joinRoom() {
addTrack(localVideoTrack, userName, true);
} catch (error) {
console.log("There was an error connecting to the room:", error.message);
await leaveRoom();
}
}
@ -170,9 +171,10 @@ async function getToken(roomName, participantName) {
});
if (!response.ok) {
throw new Error("Failed to get token");
const error = await response.json();
throw new Error(`Failed to get token: ${error.errorMessage}`);
}
const token = await response.json();
return token;
return token.token;
}

View File

@ -45,11 +45,11 @@ app.MapPost("/token", async (HttpRequest request) =>
if (bodyParams.TryGetValue("roomName", out var roomName) && bodyParams.TryGetValue("participantName", out var participantName))
{
var token = CreateLiveKitJWT(roomName.ToString(), participantName.ToString());
return Results.Json(token);
return Results.Json(new { token });
}
else
{
return Results.BadRequest("\"roomName\" and \"participantName\" are required");
return Results.BadRequest(new { errorMessage = "roomName and participantName are required" });
}
});
@ -66,7 +66,6 @@ app.MapPost("/webhook", async (HttpRequest request) =>
try
{
VerifyWebhookEvent(authHeader.First(), postData);
}
catch (Exception e)
{

View File

@ -0,0 +1,24 @@
# Basic PHP
Basic server application built for .NET with ASP.NET Core.
For further information, check the [tutorial documentation](https://livekit-tutorials.openvidu.io/tutorials/application-server/dotnet/).
## Prerequisites
- [.NET](https://dotnet.microsoft.com/en-us/download)
## Run
1. Download repository
```bash
git clone https://github.com/OpenVidu/openvidu-livekit-tutorials.git
cd openvidu-livekit-tutorials/application-server/dotnet
```
2. Run the application
```bash
dotnet run
```

View File

@ -36,7 +36,7 @@ func createToken(context *gin.Context) {
}
if body.RoomName == "" || body.ParticipantName == "" {
context.JSON(http.StatusBadRequest, "roomName and participantName are required")
context.JSON(http.StatusBadRequest, gin.H{"errorMessage": "roomName and participantName are required"})
return
}
@ -53,7 +53,7 @@ func createToken(context *gin.Context) {
return
}
context.JSON(http.StatusOK, token)
context.JSON(http.StatusOK, gin.H{"token": token})
}
func receiveWebhook(context *gin.Context) {

View File

@ -6,7 +6,7 @@
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version>
<version>3.2.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

View File

@ -14,7 +14,7 @@ import io.livekit.server.AccessToken;
import io.livekit.server.RoomJoin;
import io.livekit.server.RoomName;
import io.livekit.server.WebhookReceiver;
import livekit.LivekitWebhook;
import livekit.LivekitWebhook.WebhookEvent;
@CrossOrigin(origins = "*")
@RestController
@ -28,15 +28,15 @@ public class Controller {
/**
* @param params JSON object with roomName and participantName
* @return The JWT token
* @return JSON object with the JWT token
*/
@PostMapping(value = "/token", consumes = "application/json", produces = "application/json")
public ResponseEntity<String> createToken(@RequestBody Map<String, String> params) {
@PostMapping(value = "/token")
public ResponseEntity<Map<String, String>> createToken(@RequestBody Map<String, String> params) {
String roomName = params.get("roomName");
String participantName = params.get("participantName");
if (roomName == null || participantName == null) {
return ResponseEntity.badRequest().body("\"roomName and participantName are required\"");
return ResponseEntity.badRequest().body(Map.of("errorMessage", "roomName and participantName are required"));
}
AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET);
@ -44,14 +44,14 @@ public class Controller {
token.setIdentity(participantName);
token.addGrants(new RoomJoin(true), new RoomName(roomName));
return ResponseEntity.ok("\"" + token.toJwt() + "\"");
return ResponseEntity.ok(Map.of("token", token.toJwt()));
}
@PostMapping(value = "/webhook", consumes = "application/webhook+json")
public ResponseEntity<String> receiveWebhook(@RequestHeader("Authorization") String authHeader, @RequestBody String body) {
WebhookReceiver webhookReceiver = new WebhookReceiver(LIVEKIT_API_KEY, LIVEKIT_API_SECRET);
try {
LivekitWebhook.WebhookEvent event = webhookReceiver.receive(body, authHeader);
WebhookEvent event = webhookReceiver.receive(body, authHeader);
System.out.println("LiveKit Webhook: " + event.toString());
} catch (Exception e) {
System.err.println("Error validating webhook event: " + e.getMessage());

View File

@ -18,7 +18,7 @@ app.post("/token", async (req, res) => {
const participantName = req.body.participantName;
if (!roomName || !participantName) {
res.status(400).json("roomName and participantName are required");
res.status(400).json({ errorMessage: "roomName and participantName are required" });
return;
}
@ -27,7 +27,7 @@ app.post("/token", async (req, res) => {
});
at.addGrant({ roomJoin: true, room: roomName });
const token = await at.toJwt();
res.json(token);
res.json({ token });
});
const webhookReceiver = new WebhookReceiver(

View File

@ -58,26 +58,26 @@
},
{
"name": "firebase/php-jwt",
"version": "v6.10.0",
"version": "v6.10.1",
"source": {
"type": "git",
"url": "https://github.com/firebase/php-jwt.git",
"reference": "a49db6f0a5033aef5143295342f1c95521b075ff"
"reference": "500501c2ce893c824c801da135d02661199f60c5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/a49db6f0a5033aef5143295342f1c95521b075ff",
"reference": "a49db6f0a5033aef5143295342f1c95521b075ff",
"url": "https://api.github.com/repos/firebase/php-jwt/zipball/500501c2ce893c824c801da135d02661199f60c5",
"reference": "500501c2ce893c824c801da135d02661199f60c5",
"shasum": ""
},
"require": {
"php": "^7.4||^8.0"
"php": "^8.0"
},
"require-dev": {
"guzzlehttp/guzzle": "^6.5||^7.4",
"guzzlehttp/guzzle": "^7.4",
"phpspec/prophecy-phpunit": "^2.0",
"phpunit/phpunit": "^9.5",
"psr/cache": "^1.0||^2.0",
"psr/cache": "^2.0||^3.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0"
},
@ -115,9 +115,9 @@
],
"support": {
"issues": "https://github.com/firebase/php-jwt/issues",
"source": "https://github.com/firebase/php-jwt/tree/v6.10.0"
"source": "https://github.com/firebase/php-jwt/tree/v6.10.1"
},
"time": "2023-12-01T16:26:39+00:00"
"time": "2024-05-18T18:05:11+00:00"
},
{
"name": "google/protobuf",

View File

@ -11,11 +11,12 @@ Dotenv::createImmutable(__DIR__)->safeLoad();
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Content-type: application/json");
$LIVEKIT_API_KEY = $_ENV["LIVEKIT_API_KEY"] ?? "devkey";
$LIVEKIT_API_SECRET = $_ENV["LIVEKIT_API_SECRET"] ?? "secret";
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER["REQUEST_METHOD"] === "POST" && $_SERVER["PATH_INFO"] === "/token") {
if (isset($_SERVER["REQUEST_METHOD"]) && $_SERVER["REQUEST_METHOD"] === "POST" && $_SERVER["PATH_INFO"] === "/token") {
$data = json_decode(file_get_contents("php://input"), true);
$roomName = $data["roomName"] ?? null;
@ -23,7 +24,7 @@ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER["REQUEST_METHOD"] === "POST" &
if (!$roomName || !$participantName) {
http_response_code(400);
echo json_encode("roomName and participantName are required");
echo json_encode(["errorMessage" => "roomName and participantName are required"]);
exit();
}
@ -37,19 +38,19 @@ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER["REQUEST_METHOD"] === "POST" &
->setGrant($videoGrant)
->toJwt();
echo json_encode($token);
echo json_encode(["token" => $token]);
exit();
}
$webhookReceiver = (new WebhookReceiver($LIVEKIT_API_KEY, $LIVEKIT_API_SECRET));
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER["REQUEST_METHOD"] === "POST" && $_SERVER["PATH_INFO"] === "/webhook") {
if (isset($_SERVER["REQUEST_METHOD"]) && $_SERVER["REQUEST_METHOD"] === "POST" && $_SERVER["PATH_INFO"] === "/webhook") {
$headers = getallheaders();
$authHeader = $headers['Authorization'];
$authHeader = $headers["Authorization"];
$body = file_get_contents("php://input");
try {
$event = $webhookReceiver->receive($body, $authHeader);
error_log('LiveKit Webhook:');
error_log("LiveKit Webhook:");
error_log(print_r($event->getEvent(), true));
exit();
} catch (Exception $e) {
@ -60,5 +61,5 @@ if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER["REQUEST_METHOD"] === "POST" &
}
}
echo json_encode("Unsupported endpoint or method");
echo json_encode(["errorMessage" => "Unsupported endpoint or method"]);
exit();

View File

@ -1,5 +1,5 @@
import os
from flask import Flask, request, jsonify
from flask import Flask, request
from flask_cors import CORS
from dotenv import load_dotenv
from livekit.api import AccessToken, VideoGrants, TokenVerifier, WebhookReceiver
@ -21,14 +21,14 @@ def create_token():
participant_name = request.json.get("participantName")
if not room_name or not participant_name:
return jsonify("roomName and participantName are required"), 400
return {"errorMessage": "roomName and participantName are required"}, 400
token = (
AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET)
.with_identity(participant_name)
.with_grants(VideoGrants(room_join=True, room=room_name))
)
return jsonify(token.to_jwt())
return {"token": token.to_jwt()}
token_verifier = TokenVerifier(LIVEKIT_API_KEY, LIVEKIT_API_SECRET)
@ -36,19 +36,19 @@ webhook_receiver = WebhookReceiver(token_verifier)
@app.post("/webhook")
async def receive_webhook():
def receive_webhook():
auth_token = request.headers.get("Authorization")
if not auth_token:
return jsonify("Authorization header is required"), 401
return "Authorization header is required", 401
try:
event = webhook_receiver.receive(request.data.decode("utf-8"), auth_token)
print("LiveKit Webhook:", event)
return jsonify(), 200
return "ok"
except:
print("Authorization header is not valid")
return jsonify("Authorization header is not valid"), 401
return "Authorization header is not valid", 401
if __name__ == "__main__":

View File

@ -2,6 +2,7 @@ source 'https://rubygems.org'
gem 'sinatra'
gem 'sinatra-cors'
gem 'sinatra-contrib'
gem "rackup", "~> 2.1"
gem 'puma'
gem 'livekit-server-sdk'

View File

@ -1,4 +1,5 @@
require 'sinatra'
require 'sinatra/json'
require 'sinatra/cors'
require 'livekit'
require './env.rb'
@ -22,14 +23,14 @@ post '/token' do
if room_name.nil? || participant_name.nil?
status 400
return JSON.generate('roomName and participantName are required')
return json({errorMessage: 'roomName and participantName are required'})
end
token = LiveKit::AccessToken.new(api_key: LIVEKIT_API_KEY, api_secret: LIVEKIT_API_SECRET)
token.identity = participant_name
token.add_grant(roomJoin: true, room: room_name)
return JSON.generate(token.to_jwt)
return json({token: token.to_jwt})
end
post '/webhook' do
@ -39,7 +40,7 @@ post '/webhook' do
token_verifier.verify(auth_header)
body = JSON.parse(request.body.read)
puts "LiveKit Webhook: #{body}"
return JSON.generate('ok')
return
rescue => e
puts "Authorization header is not valid: #{e}"
end

View File

@ -1,17 +1,23 @@
#### Prerequisites
# Basic PHP
To run this application you will need **Rust**:
Basic server application built for Rust with Axum. It internally uses [livekit-sdk-rust](https://crates.io/crates/livekit).
- [Rust](https://www.rust-lang.org/tools/install){:target="\_blank"}
For further information, check the [tutorial documentation](https://livekit-tutorials.openvidu.io/tutorials/application-server/rust/).
#### Download repository
## Prerequisites
- [Rust](https://www.rust-lang.org/tools/install)
## Run
1. Download repository
```bash
git clone https://github.com/OpenVidu/openvidu-livekit-tutorials.git
cd openvidu-livekit-tutorials/application-server/rust
```
#### Run application
2. Run the application
```bash
cargo run

View File

@ -8,18 +8,16 @@ use livekit_api::access_token::AccessToken;
use livekit_api::access_token::TokenVerifier;
use livekit_api::access_token::VideoGrants;
use livekit_api::webhooks::WebhookReceiver;
use serde_json::Value;
use serde_json::{json, Value};
use std::env;
use tokio::net::TcpListener;
use tower_http::cors::{Any, CorsLayer};
#[tokio::main]
async fn main() {
dotenv().ok(); // Load environment variables from .env
// Check that required environment variables are set
let server_port = env::var("SERVER_PORT").unwrap_or("6080".to_string());
env::var("LIVEKIT_API_KEY").expect("LIVEKIT_API_KEY is not set");
env::var("LIVEKIT_API_SECRET").expect("LIVEKIT_API_SECRET is not set");
let server_port = env::var("SERVER_PORT").unwrap_or("6081".to_string());
let cors = CorsLayer::new()
.allow_methods([Method::POST])
@ -31,24 +29,37 @@ async fn main() {
.route("/webhook", post(receive_webhook))
.layer(cors);
let listener = tokio::net::TcpListener::bind("0.0.0.0:".to_string() + &server_port)
let listener = TcpListener::bind("0.0.0.0:".to_string() + &server_port)
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn create_token(payload: Option<Json<Value>>) -> (StatusCode, Json<String>) {
async fn create_token(payload: Option<Json<Value>>) -> (StatusCode, Json<Value>) {
if let Some(payload) = payload {
let livekit_api_key = env::var("LIVEKIT_API_KEY").expect("LIVEKIT_API_KEY is not set");
let livekit_api_secret =
env::var("LIVEKIT_API_SECRET").expect("LIVEKIT_API_SECRET is not set");
let livekit_api_key = env::var("LIVEKIT_API_KEY").unwrap_or("devkey".to_string());
let livekit_api_secret = env::var("LIVEKIT_API_SECRET").unwrap_or("secret".to_string());
let room_name = payload.get("roomName").expect("roomName is required");
let participant_name = payload
.get("participantName")
.expect("participantName is required");
let room_name = match payload.get("roomName") {
Some(value) => value,
None => {
return (
StatusCode::BAD_REQUEST,
Json(json!({ "errorMessage": "roomName is required" })),
);
}
};
let participant_name = match payload.get("participantName") {
Some(value) => value,
None => {
return (
StatusCode::BAD_REQUEST,
Json(json!({ "errorMessage": "participantName is required" })),
);
}
};
let token = AccessToken::with_api_key(&livekit_api_key, &livekit_api_secret)
let token = match AccessToken::with_api_key(&livekit_api_key, &livekit_api_secret)
.with_identity(&participant_name.to_string())
.with_name(&participant_name.to_string())
.with_grants(VideoGrants {
@ -57,41 +68,59 @@ async fn create_token(payload: Option<Json<Value>>) -> (StatusCode, Json<String>
..Default::default()
})
.to_jwt()
.unwrap();
{
Ok(token) => token,
Err(_) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({ "errorMessage": "Error creating token" })),
);
}
};
println!(
"Sending token for room {} to participant {}",
room_name, participant_name
);
return (StatusCode::OK, Json(token));
return (StatusCode::OK, Json(json!({ "token": token })));
} else {
return (
StatusCode::BAD_REQUEST,
Json("roomName and participantName are required".to_string()),
Json(json!({ "errorMessage": "roomName and participantName are required" })),
);
}
}
async fn receive_webhook(headers: HeaderMap, body: String) -> StatusCode {
let livekit_api_key = env::var("LIVEKIT_API_KEY").expect("LIVEKIT_API_KEY is not set");
let livekit_api_secret = env::var("LIVEKIT_API_SECRET").expect("LIVEKIT_API_SECRET is not set");
async fn receive_webhook(headers: HeaderMap, body: String) -> (StatusCode, String) {
let livekit_api_key = env::var("LIVEKIT_API_KEY").unwrap_or("devkey".to_string());
let livekit_api_secret = env::var("LIVEKIT_API_SECRET").unwrap_or("secret".to_string());
let token_verifier = TokenVerifier::with_api_key(&livekit_api_key, &livekit_api_secret);
let webhook_receiver = WebhookReceiver::new(token_verifier);
let auth_header: &str = headers
.get("Authorization")
.expect("Authorization header is required")
.to_str()
.unwrap();
let auth_header = match headers.get("Authorization") {
Some(header_value) => match header_value.to_str() {
Ok(header_str) => header_str,
Err(_) => {
return (
StatusCode::BAD_REQUEST,
"Invalid Authorization header format".to_string(),
);
}
},
None => {
return (
StatusCode::BAD_REQUEST,
"Authorization header is required".to_string(),
);
}
};
let res = webhook_receiver.receive(&body, auth_header);
if let Ok(event) = res {
println!("LiveKit WebHook: {:?}", event);
return StatusCode::OK;
} else {
println!("Error validating webhook event: {:?}", res);
return StatusCode::UNAUTHORIZED;
match webhook_receiver.receive(&body, auth_header) {
Ok(event) => {
println!("LiveKit WebHook: {:?}", event);
return (StatusCode::OK, "ok".to_string());
}
Err(_) => {
return (
StatusCode::UNAUTHORIZED,
"Error validating webhook event".to_string(),
);
}
}
}