From 57588c7e1321e168298137316d14108f27cc3359 Mon Sep 17 00:00:00 2001 From: Miguel da Mota Date: Mon, 1 Jan 2024 16:26:44 +0100 Subject: [PATCH] [API-1] feat: search and spotify service --- Cargo.lock | 370 +++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/main.rs | 2 + src/models/mod.rs | 1 + src/models/spotify.rs | 5 + src/routes/mod.rs | 1 + src/routes/search.rs | 26 +++ src/services/mod.rs | 2 + src/services/service.rs | 9 + src/services/spotify.rs | 82 +++++++++ 10 files changed, 495 insertions(+), 4 deletions(-) create mode 100644 src/models/spotify.rs create mode 100644 src/routes/search.rs create mode 100644 src/services/mod.rs create mode 100644 src/services/service.rs create mode 100644 src/services/spotify.rs diff --git a/Cargo.lock b/Cargo.lock index 38e001b..f8730de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -437,6 +437,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -572,6 +582,22 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "flate2" version = "1.0.28" @@ -588,6 +614,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -597,6 +638,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + [[package]] name = "futures-core" version = "0.3.30" @@ -701,6 +751,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.8.0" @@ -713,6 +774,43 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.59" @@ -765,6 +863,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itoa" version = "1.0.10" @@ -822,6 +926,12 @@ version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" + [[package]] name = "local-channel" version = "0.1.5" @@ -906,7 +1016,25 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] @@ -954,6 +1082,50 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.44", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -1135,6 +1307,44 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "reqwest" +version = "0.11.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "ring" version = "0.17.7" @@ -1146,7 +1356,7 @@ dependencies = [ "libc", "spin", "untrusted", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1164,12 +1374,34 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scheduled-thread-pool" version = "0.2.7" @@ -1185,6 +1417,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.20" @@ -1308,7 +1563,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -1345,6 +1600,40 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "thiserror" version = "1.0.53" @@ -1423,7 +1712,17 @@ dependencies = [ "pin-project-lite", "signal-hook-registry", "socket2", - "windows-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", ] [[package]] @@ -1474,6 +1773,12 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" @@ -1494,6 +1799,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.17.0" @@ -1565,11 +1876,21 @@ dependencies = [ "hmac", "jsonwebtoken", "lazy_static", + "reqwest", "serde", "serde_json", "sha2", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1601,6 +1922,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.89" @@ -1630,6 +1963,16 @@ version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -1648,6 +1991,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -1771,6 +2123,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "zerocopy" version = "0.7.32" diff --git a/Cargo.toml b/Cargo.toml index c8a5e4e..40fae80 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ diesel = { version = "2", features = ["r2d2", "postgres", "chrono"] } diesel_migrations = "2" dotenvy = "*" hmac = "0.12" +reqwest = { version = "0.11", features = ["json"] } sha2 = "0.10" lazy_static = "1" jsonwebtoken = "9" diff --git a/src/main.rs b/src/main.rs index aaa20f1..8705e46 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ mod middlewares; mod models; mod routes; mod schema; +mod services; use crate::helpers::db; use actix_web::{web, App, HttpServer}; @@ -22,6 +23,7 @@ async fn main() -> std::io::Result<()> { .service(routes::auth::login) .service(routes::me::routes()) .service(routes::users::routes()) + .service(routes::search::routes()) }) .bind(("127.0.0.1", 9000))? .run() diff --git a/src/models/mod.rs b/src/models/mod.rs index 048b749..62087e1 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,4 +1,5 @@ pub mod artists; pub mod playlists; +pub mod spotify; pub mod tracks; pub mod user; diff --git a/src/models/spotify.rs b/src/models/spotify.rs new file mode 100644 index 0000000..0145ab1 --- /dev/null +++ b/src/models/spotify.rs @@ -0,0 +1,5 @@ +pub struct Track {} + +pub struct Artist {} + +pub struct Album {} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 339a10d..f643e8b 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,4 +1,5 @@ pub mod auth; pub mod me; pub mod playlists; +pub mod search; pub mod users; diff --git a/src/routes/search.rs b/src/routes/search.rs new file mode 100644 index 0000000..6528aed --- /dev/null +++ b/src/routes/search.rs @@ -0,0 +1,26 @@ +use crate::middlewares::error::ErrorResponse; +use crate::services::spotify; +use actix_web::{get, web, HttpResponse, Result, Scope}; +use serde::{Deserialize, Serialize}; + +pub fn routes() -> Scope { + web::scope("/search").service(search) +} + +#[derive(Deserialize)] +struct SearchQuery { + q: String, +} + +#[derive(Serialize)] +struct SearchResponse { + track: (), +} + +#[get("")] +async fn search(query: web::Query) -> Result { + let spotify = spotify::instance().await; + let track = spotify.search(&query.q).await; + + Ok(HttpResponse::Ok().json(SearchResponse { track })) +} diff --git a/src/services/mod.rs b/src/services/mod.rs new file mode 100644 index 0000000..1204d8d --- /dev/null +++ b/src/services/mod.rs @@ -0,0 +1,2 @@ +mod service; +pub mod spotify; diff --git a/src/services/service.rs b/src/services/service.rs new file mode 100644 index 0000000..9f6807b --- /dev/null +++ b/src/services/service.rs @@ -0,0 +1,9 @@ +use async_trait::async_trait; + +#[async_trait] +pub trait Service: Send + Sync { + fn get_token(&self) -> &String; + + async fn fetch_token(&mut self) -> &String; + // async fn get_track_by_isrc(&self, isrc: &str); +} diff --git a/src/services/spotify.rs b/src/services/spotify.rs new file mode 100644 index 0000000..0cfd332 --- /dev/null +++ b/src/services/spotify.rs @@ -0,0 +1,82 @@ +use crate::services::service::Service; +use actix_web::http::Method; +use async_trait::async_trait; +use reqwest::{Client, RequestBuilder}; +use serde::Deserialize; + +#[derive(Deserialize)] +struct SpotifyResponse { + access_token: String, +} + +#[derive(Clone)] +pub struct Spotify { + token: String, +} + +pub async fn instance<'a>() -> Spotify { + Spotify::new().await +} + +impl Spotify { + pub async fn new() -> Spotify { + let mut spotify = Spotify { + token: String::new(), + }; + spotify.fetch_token().await; + spotify + } + + fn api(&self, url: &str, method: Method) -> RequestBuilder { + let client = Client::new(); + + client + .request(method, &format!("https://api.spotify.com/v1{url}")) + .bearer_auth(self.get_token()) + } + + // pub async fn get_track(self, spotify_id: &str) { + // let spotify = &SPOTIFY; + // } + + pub async fn get_artist(self, spotify_id: &str) {} + + pub async fn get_track_by_isrc(self, isrc: &str) {} + + pub async fn search(self, search: &str) {} +} + +#[async_trait] +impl Service for Spotify { + fn get_token(&self) -> &String { + &self.token + } + + async fn fetch_token(&mut self) -> &String { + let response = Client::new() + .post("https://accounts.spotify.com/api/token") + .basic_auth( + get_env("SPOTIFY_CLIENT_ID"), + Some(get_env("SPOTIFY_CLIENT_SECRET")), + ) + .form(&[("grant_type", "client_credentials")]) + .send() + .await; + + match response { + Ok(res) => { + let data: Result = res.json().await; + + self.token = data.unwrap().access_token.into(); + &self.token + } + Err(_) => { + panic!("Invalid response (Spotify Token)") + } + } + } +} + +fn get_env(key: &str) -> String { + std::env::var(key).unwrap() +}