mod columns;
mod error;
mod options;
pub use columns::ALLOWED_COLUMNS;
pub use error::MinterError;
pub use options::{TokenOptions, MAX_EXPIRES_IN, MIN_EXPIRES_IN};
use std::time::Duration;
use serde::Deserialize;
pub const DEFAULT_BASE_URL: &str = "https://api.everscribe.io";
pub const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(10);
#[derive(Clone, Debug)]
pub struct MinterOptions {
pub base_url: String,
pub request_timeout: Duration,
}
impl Default for MinterOptions {
fn default() -> Self {
MinterOptions {
base_url: DEFAULT_BASE_URL.to_string(),
request_timeout: DEFAULT_REQUEST_TIMEOUT,
}
}
}
#[derive(Clone)]
pub struct Client {
base_url: String,
project_id: String,
api_key: String,
http: reqwest::Client,
}
impl Client {
pub fn new(
project_id: impl Into<String>,
api_key: impl Into<String>,
opts: MinterOptions,
) -> Self {
let http = reqwest::Client::builder()
.timeout(opts.request_timeout)
.build()
.expect("failed to build reqwest client");
Client {
base_url: opts.base_url.trim_end_matches('/').to_string(),
project_id: project_id.into(),
api_key: api_key.into(),
http,
}
}
pub async fn mint_token(&self, opts: &TokenOptions) -> Result<String, MinterError> {
let body = opts.to_wire()?;
let url = format!(
"{}/v1/projects/{}/embed-tokens",
self.base_url, self.project_id
);
let resp = self
.http
.post(&url)
.bearer_auth(&self.api_key)
.json(&body)
.send()
.await?;
let status = resp.status();
if status.is_success() {
let parsed: TokenResponse = resp
.json()
.await
.map_err(|e| MinterError::Decode(e.to_string()))?;
return Ok(parsed.token);
}
let text = resp.text().await.unwrap_or_default();
Err(MinterError::Http {
status: status.as_u16(),
body: text.trim().to_string(),
})
}
}
#[derive(Deserialize)]
struct TokenResponse {
token: String,
}