api.rs

  1use crate::{proto, token};
  2use anyhow::{anyhow, Result};
  3use prost::Message;
  4use reqwest::header::CONTENT_TYPE;
  5use std::future::Future;
  6
  7pub struct Client {
  8    http: reqwest::Client,
  9    uri: String,
 10    key: String,
 11    secret: String,
 12}
 13
 14impl Client {
 15    pub fn new(mut uri: String, key: String, secret: String) -> Self {
 16        if uri.ends_with('/') {
 17            uri.pop();
 18        }
 19
 20        Self {
 21            http: reqwest::Client::new(),
 22            uri,
 23            key,
 24            secret,
 25        }
 26    }
 27
 28    pub fn create_room(&self, name: String) -> impl Future<Output = Result<proto::Room>> {
 29        self.request(
 30            "twirp/livekit.RoomService/CreateRoom",
 31            token::VideoGrant {
 32                room_create: Some(true),
 33                ..Default::default()
 34            },
 35            proto::CreateRoomRequest {
 36                name,
 37                ..Default::default()
 38            },
 39        )
 40    }
 41
 42    pub fn delete_room(&self, name: String) -> impl Future<Output = Result<()>> {
 43        let response = self.request(
 44            "twirp/livekit.RoomService/DeleteRoom",
 45            token::VideoGrant {
 46                room_create: Some(true),
 47                ..Default::default()
 48            },
 49            proto::DeleteRoomRequest { room: name },
 50        );
 51        async move {
 52            response.await?;
 53            Ok(())
 54        }
 55    }
 56
 57    pub fn room_token_for_user(&self, room: &str, identity: &str) -> Result<String> {
 58        token::create(
 59            &self.key,
 60            &self.secret,
 61            Some(identity),
 62            token::VideoGrant {
 63                room: Some(room),
 64                room_join: Some(true),
 65                can_publish: Some(true),
 66                can_subscribe: Some(true),
 67                ..Default::default()
 68            },
 69        )
 70    }
 71
 72    fn request<Req, Res>(
 73        &self,
 74        path: &str,
 75        grant: token::VideoGrant,
 76        body: Req,
 77    ) -> impl Future<Output = Result<Res>>
 78    where
 79        Req: Message,
 80        Res: Default + Message,
 81    {
 82        let client = self.http.clone();
 83        let token = token::create(&self.key, &self.secret, None, grant);
 84        let uri = format!("{}/{}", self.uri, path);
 85        async move {
 86            let token = token?;
 87            let response = client
 88                .post(&uri)
 89                .header(CONTENT_TYPE, "application/protobuf")
 90                .bearer_auth(token)
 91                .body(body.encode_to_vec())
 92                .send()
 93                .await?;
 94            if response.status().is_success() {
 95                Ok(Res::decode(response.bytes().await?)?)
 96            } else {
 97                Err(anyhow!(
 98                    "POST {} failed with status code {:?}, {:?}",
 99                    uri,
100                    response.status(),
101                    response.text().await
102                ))
103            }
104        }
105    }
106}