use serde::{Deserialize, Serialize};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OAuthMode {
Max,
Console,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenSet {
pub access_token: String,
pub refresh_token: String,
pub expires_at: u64,
}
impl TokenSet {
pub fn is_expired(&self) -> bool {
self.expires_in() <= Duration::from_secs(300)
}
pub fn expires_in(&self) -> Duration {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
if self.expires_at > now {
Duration::from_secs(self.expires_at - now)
} else {
Duration::ZERO
}
}
pub fn validate(&self) -> Result<(), &'static str> {
if self.access_token.is_empty() {
return Err("access_token is empty");
}
if self.refresh_token.is_empty() {
return Err("refresh_token is empty");
}
if self.expires_at == 0 {
return Err("expires_at is invalid");
}
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs();
if self.expires_at > now + 31536000 {
return Err("expires_at is too far in the future");
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct OAuthFlow {
pub authorization_url: String,
pub verifier: String,
pub state: String,
pub mode: OAuthMode,
}
#[derive(Debug, Clone)]
pub struct OAuthConfig {
pub client_id: String,
pub redirect_uri: String,
}
impl Default for OAuthConfig {
fn default() -> Self {
Self {
client_id: "9d1c250a-e61b-44d9-88ed-5944d1962f5e".to_string(),
redirect_uri: "http://localhost:1455/callback".to_string(),
}
}
}
impl OAuthConfig {
pub fn builder() -> OAuthConfigBuilder {
OAuthConfigBuilder::default()
}
}
#[derive(Debug, Clone, Default)]
pub struct OAuthConfigBuilder {
client_id: Option<String>,
redirect_uri: Option<String>,
}
impl OAuthConfigBuilder {
pub fn client_id(mut self, client_id: impl Into<String>) -> Self {
self.client_id = Some(client_id.into());
self
}
pub fn redirect_uri(mut self, redirect_uri: impl Into<String>) -> Self {
self.redirect_uri = Some(redirect_uri.into());
self
}
pub fn redirect_port(mut self, port: u16) -> Self {
self.redirect_uri = Some(format!("http://localhost:{}/callback", port));
self
}
pub fn build(self) -> OAuthConfig {
let defaults = OAuthConfig::default();
OAuthConfig {
client_id: self.client_id.unwrap_or(defaults.client_id),
redirect_uri: self.redirect_uri.unwrap_or(defaults.redirect_uri),
}
}
}
#[derive(Debug, Deserialize)]
pub(crate) struct TokenResponse {
pub access_token: String,
pub refresh_token: Option<String>,
pub expires_in: Option<u64>,
}
impl From<TokenResponse> for TokenSet {
fn from(response: TokenResponse) -> Self {
let expires_at = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
+ response.expires_in.unwrap_or(3600);
TokenSet {
access_token: response.access_token,
refresh_token: response.refresh_token.unwrap_or_default(),
expires_at,
}
}
}
#[derive(Debug, Deserialize)]
pub(crate) struct ApiKeyResponse {
pub raw_key: String,
}