livekit_api/services/
mod.rs1use std::collections::HashMap;
16use std::fmt::{Debug, Display};
17
18use http::header::{HeaderMap, HeaderValue, AUTHORIZATION};
19use thiserror::Error;
20
21use livekit_token::{AccessToken, AccessTokenError, SIPGrants, VideoGrants};
22
23pub use crate::public_api::public_api_url;
24pub use livekit_api::LiveKitApi;
25pub use twirp_client::{
26 ServerError,
27 ServerErrorCode,
28 ServerResult,
29 TwirpError,
31 TwirpErrorCode,
32 TwirpResult,
33};
34
35pub mod agent_dispatch;
36pub mod connector;
37pub mod databases;
38pub mod egress;
39pub mod ingress;
40pub mod room;
41pub mod sip;
42pub mod threads;
43
44mod dial_timeout;
45mod failover;
46mod livekit_api;
47mod twirp_client;
48
49#[cfg(all(test, feature = "services"))]
50mod api_test;
51
52pub const LIVEKIT_PACKAGE: &str = "livekit";
53
54#[derive(Debug, Error)]
55pub enum ServiceError {
56 #[error("invalid environment: {0}")]
57 Env(#[from] std::env::VarError),
58 #[error("invalid access token: {0}")]
59 AccessToken(#[from] AccessTokenError),
60 #[error("server error: {0}")]
61 Twirp(#[from] ServerError),
62}
63
64pub type ServiceResult<T> = Result<T, ServiceError>;
65
66#[derive(Clone)]
69pub(crate) enum ServiceBase {
70 ApiKeySecret { api_key: String, api_secret: String },
72 Token(String),
75}
76
77impl Debug for ServiceBase {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 match self {
81 Self::ApiKeySecret { api_key, .. } => {
82 f.debug_struct("ServiceBase").field("api_key", api_key).finish_non_exhaustive()
83 }
84 Self::Token(_) => f.debug_struct("ServiceBase").field("token", &"<redacted>").finish(),
85 }
86 }
87}
88
89impl ServiceBase {
90 pub fn with_api_key(api_key: &str, api_secret: &str) -> Self {
91 Self::ApiKeySecret { api_key: api_key.to_owned(), api_secret: api_secret.to_owned() }
92 }
93
94 pub fn with_token(token: &str) -> Self {
95 Self::Token(token.to_owned())
96 }
97
98 pub fn auth_header(
99 &self,
100 grants: VideoGrants,
101 sip: Option<SIPGrants>,
102 ) -> Result<HeaderMap, AccessTokenError> {
103 let token = match self {
104 Self::Token(token) => token.clone(),
105 Self::ApiKeySecret { api_key, api_secret } => {
106 let mut tok = AccessToken::with_api_key(api_key, api_secret).with_grants(grants);
107 if let Some(sip_grants) = sip {
108 tok = tok.with_sip_grants(sip_grants);
109 }
110 tok.to_jwt()?
111 }
112 };
113
114 let mut headers = HeaderMap::new();
115 headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}", token)).unwrap());
116 Ok(headers)
117 }
118}
119
120#[derive(Debug, Clone)]
124pub struct SipCallError {
125 code: String,
126 sip_status_code: Option<i32>,
127 sip_status: Option<String>,
128 metadata: HashMap<String, String>,
129}
130
131impl SipCallError {
132 pub fn from_error(err: &ServiceError) -> Option<Self> {
135 let ServiceError::Twirp(ServerError::Twirp(code)) = err else {
136 return None;
137 };
138 if !code.meta.contains_key("sip_status_code") && !code.meta.contains_key("sip_status") {
139 return None;
140 }
141 Some(Self {
142 code: code.code.clone(),
143 sip_status_code: code.meta.get("sip_status_code").and_then(|v| v.parse().ok()),
144 sip_status: code.meta.get("sip_status").cloned(),
145 metadata: code.meta.clone(),
146 })
147 }
148
149 pub fn code(&self) -> &str {
151 &self.code
152 }
153
154 pub fn sip_status_code(&self) -> Option<i32> {
156 self.sip_status_code
157 }
158
159 pub fn sip_status(&self) -> Option<&str> {
161 self.sip_status.as_deref()
162 }
163
164 pub fn metadata(&self) -> &HashMap<String, String> {
166 &self.metadata
167 }
168}
169
170impl Display for SipCallError {
171 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172 write!(f, "SIP call failed: {}", self.sip_status_code.unwrap_or_default())?;
173 if let Some(reason) = &self.sip_status {
174 write!(f, " {}", reason)?;
175 }
176 write!(f, " ({})", self.code)?;
177 let mut extra: Vec<_> = self
178 .metadata
179 .iter()
180 .filter(|(k, _)| {
181 !matches!(k.as_str(), "sip_status_code" | "sip_status" | "error_details")
182 })
183 .map(|(k, v)| format!("{}={}", k, v))
184 .collect();
185 if !extra.is_empty() {
186 extra.sort();
187 write!(f, " [{}]", extra.join(", "))?;
188 }
189 Ok(())
190 }
191}
192
193impl std::error::Error for SipCallError {}