Spotify-tracker/src/models.rs
Benjamin Slingo e09e8b2d67 Implement bulletproof OAuth code reuse prevention
- 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
2025-08-30 23:35:20 -04:00

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(),
})
}
}