api.rs

  1use crate::{proto, token};
  2use anyhow::{anyhow, Result};
  3use async_trait::async_trait;
  4use prost::Message;
  5use reqwest::header::CONTENT_TYPE;
  6use std::{future::Future, sync::Arc};
  7
  8#[async_trait]
  9pub trait Client: Send + Sync {
 10    fn url(&self) -> &str;
 11    async fn create_room(&self, name: String) -> Result<()>;
 12    async fn delete_room(&self, name: String) -> Result<()>;
 13    async fn remove_participant(&self, room: String, identity: String) -> Result<()>;
 14    fn room_token(&self, room: &str, identity: &str) -> Result<String>;
 15}
 16
 17#[derive(Clone)]
 18pub struct LiveKitClient {
 19    http: reqwest::Client,
 20    url: Arc<str>,
 21    key: Arc<str>,
 22    secret: Arc<str>,
 23}
 24
 25impl LiveKitClient {
 26    pub fn new(mut url: String, key: String, secret: String) -> Self {
 27        if url.ends_with('/') {
 28            url.pop();
 29        }
 30
 31        Self {
 32            http: reqwest::Client::new(),
 33            url: url.into(),
 34            key: key.into(),
 35            secret: secret.into(),
 36        }
 37    }
 38
 39    fn request<Req, Res>(
 40        &self,
 41        path: &str,
 42        grant: token::VideoGrant,
 43        body: Req,
 44    ) -> impl Future<Output = Result<Res>>
 45    where
 46        Req: Message,
 47        Res: Default + Message,
 48    {
 49        let client = self.http.clone();
 50        let token = token::create(&self.key, &self.secret, None, grant);
 51        let url = format!("{}/{}", self.url, path);
 52        log::info!("Request {}: {:?}", url, body);
 53        async move {
 54            let token = token?;
 55            let response = client
 56                .post(&url)
 57                .header(CONTENT_TYPE, "application/protobuf")
 58                .bearer_auth(token)
 59                .body(body.encode_to_vec())
 60                .send()
 61                .await?;
 62
 63            if response.status().is_success() {
 64                log::info!("Response {}: {:?}", url, response.status());
 65                Ok(Res::decode(response.bytes().await?)?)
 66            } else {
 67                log::error!("Response {}: {:?}", url, response.status());
 68                Err(anyhow!(
 69                    "POST {} failed with status code {:?}, {:?}",
 70                    url,
 71                    response.status(),
 72                    response.text().await
 73                ))
 74            }
 75        }
 76    }
 77}
 78
 79#[async_trait]
 80impl Client for LiveKitClient {
 81    fn url(&self) -> &str {
 82        &self.url
 83    }
 84
 85    async fn create_room(&self, name: String) -> Result<()> {
 86        let x: proto::Room = self
 87            .request(
 88                "twirp/livekit.RoomService/CreateRoom",
 89                token::VideoGrant {
 90                    room_create: Some(true),
 91                    ..Default::default()
 92                },
 93                proto::CreateRoomRequest {
 94                    name,
 95                    ..Default::default()
 96                },
 97            )
 98            .await?;
 99        dbg!(x);
100        Ok(())
101    }
102
103    async fn delete_room(&self, name: String) -> Result<()> {
104        let _: proto::DeleteRoomResponse = self
105            .request(
106                "twirp/livekit.RoomService/DeleteRoom",
107                token::VideoGrant {
108                    room_create: Some(true),
109                    ..Default::default()
110                },
111                proto::DeleteRoomRequest { room: name },
112            )
113            .await?;
114        Ok(())
115    }
116
117    async fn remove_participant(&self, room: String, identity: String) -> Result<()> {
118        let _: proto::RemoveParticipantResponse = self
119            .request(
120                "twirp/livekit.RoomService/RemoveParticipant",
121                token::VideoGrant::to_admin(&room),
122                proto::RoomParticipantIdentity {
123                    room: room.clone(),
124                    identity,
125                },
126            )
127            .await?;
128        Ok(())
129    }
130
131    fn room_token(&self, room: &str, identity: &str) -> Result<String> {
132        token::create(
133            &self.key,
134            &self.secret,
135            Some(identity),
136            token::VideoGrant::to_join(room),
137        )
138    }
139}