
- Add state parameter generation and validation with crypto-secure random values - Implement used authorization code tracking to prevent replay attacks - Add automatic redirect after successful auth to prevent refresh issues - Enhance OAuth callback with comprehensive security checks - Fix route conflicts between home page and OAuth callback handling - Add rand dependency for secure state generation - Update models.rs to handle optional Spotify API fields - Improve error messages and logging for security violations
167 lines
4.5 KiB
Rust
167 lines
4.5 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
use chrono::{DateTime, Utc};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SpotifyConfig {
|
|
pub client_id: String,
|
|
pub client_secret: String,
|
|
pub redirect_uri: String,
|
|
pub scopes: Vec<String>,
|
|
}
|
|
|
|
impl Default for SpotifyConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
client_id: String::new(),
|
|
client_secret: String::new(),
|
|
redirect_uri: "https://spotify.tougie.live".to_string(),
|
|
scopes: vec![
|
|
"user-read-currently-playing".to_string(),
|
|
"user-read-playback-state".to_string(),
|
|
],
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct TokenInfo {
|
|
pub access_token: String,
|
|
pub refresh_token: Option<String>,
|
|
pub expires_at: DateTime<Utc>,
|
|
pub token_type: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct TokenResponse {
|
|
pub access_token: String,
|
|
pub token_type: String,
|
|
pub scope: String,
|
|
pub expires_in: u64,
|
|
pub refresh_token: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Artist {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub external_urls: ExternalUrls,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Album {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub artists: Vec<Artist>,
|
|
pub external_urls: ExternalUrls,
|
|
pub images: Vec<Image>,
|
|
pub release_date: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Track {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub artists: Vec<Artist>,
|
|
pub album: Album,
|
|
pub duration_ms: u64,
|
|
pub explicit: bool,
|
|
pub external_urls: ExternalUrls,
|
|
pub popularity: Option<u32>,
|
|
pub preview_url: Option<String>,
|
|
pub track_number: u32,
|
|
pub is_local: bool,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct ExternalUrls {
|
|
pub spotify: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Image {
|
|
pub url: String,
|
|
pub height: Option<u32>,
|
|
pub width: Option<u32>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Context {
|
|
pub external_urls: ExternalUrls,
|
|
pub href: String,
|
|
#[serde(rename = "type")]
|
|
pub context_type: String,
|
|
pub uri: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Device {
|
|
pub id: Option<String>,
|
|
pub is_active: bool,
|
|
pub is_private_session: bool,
|
|
pub is_restricted: bool,
|
|
pub name: String,
|
|
#[serde(rename = "type")]
|
|
pub device_type: String,
|
|
pub volume_percent: Option<u32>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CurrentTrack {
|
|
pub timestamp: u64,
|
|
pub context: Option<Context>,
|
|
pub progress_ms: Option<u64>,
|
|
pub item: Option<Track>,
|
|
pub currently_playing_type: String,
|
|
pub actions: Actions,
|
|
pub is_playing: bool,
|
|
pub device: Option<Device>,
|
|
pub repeat_state: Option<String>,
|
|
pub shuffle_state: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Actions {
|
|
pub interrupting_playback: Option<bool>,
|
|
pub pausing: Option<bool>,
|
|
pub resuming: Option<bool>,
|
|
pub seeking: Option<bool>,
|
|
pub skipping_next: Option<bool>,
|
|
pub skipping_prev: Option<bool>,
|
|
pub toggling_repeat_context: Option<bool>,
|
|
pub toggling_shuffle: Option<bool>,
|
|
pub toggling_repeat_track: Option<bool>,
|
|
pub transferring_playback: Option<bool>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct SimplifiedCurrentTrack {
|
|
pub track_name: String,
|
|
pub artist_name: String,
|
|
pub album_name: String,
|
|
pub is_playing: bool,
|
|
pub progress_ms: Option<u64>,
|
|
pub duration_ms: u64,
|
|
pub external_url: String,
|
|
pub image_url: Option<String>,
|
|
pub timestamp: DateTime<Utc>,
|
|
}
|
|
|
|
impl From<CurrentTrack> for Option<SimplifiedCurrentTrack> {
|
|
fn from(current: CurrentTrack) -> Self {
|
|
let track = current.item?;
|
|
let artist_names: Vec<String> = track.artists.iter().map(|a| a.name.clone()).collect();
|
|
let image_url = track.album.images.first().map(|img| img.url.clone());
|
|
|
|
Some(SimplifiedCurrentTrack {
|
|
track_name: track.name,
|
|
artist_name: artist_names.join(", "),
|
|
album_name: track.album.name,
|
|
is_playing: current.is_playing,
|
|
progress_ms: current.progress_ms,
|
|
duration_ms: track.duration_ms,
|
|
external_url: track.external_urls.spotify,
|
|
image_url,
|
|
timestamp: Utc::now(),
|
|
})
|
|
}
|
|
} |