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}