From f93e034373d942b426acc75227641ea6b538c33b Mon Sep 17 00:00:00 2001 From: Jonas_Jones <91549607+J-onasJones@users.noreply.github.com> Date: Wed, 13 Dec 2023 04:19:04 +0100 Subject: [PATCH] some updates --- Cargo.lock | 59 +++++++++++++++++++++- Cargo.toml | 5 +- README.md | 36 ++++++++++++- config.toml | 10 ++++ src/config/mod.rs | 84 +++++++++++++++++++++++++++++++ src/db/apps/mod.rs | 1 + src/db/mod.rs | 0 src/db/users/mod.rs | 0 src/v1/builtin/mod.rs | 13 +---- src/v1/kcomebacks/mod.rs | 62 +++++++++++++++++++++-- src/v1/kcomebacks/upcoming/mod.rs | 8 +++ src/v1/mod.rs | 7 ++- 12 files changed, 265 insertions(+), 20 deletions(-) create mode 100644 config.toml create mode 100644 src/config/mod.rs create mode 100644 src/db/apps/mod.rs create mode 100644 src/db/mod.rs create mode 100644 src/db/users/mod.rs create mode 100644 src/v1/kcomebacks/upcoming/mod.rs diff --git a/Cargo.lock b/Cargo.lock index ccb6216..1b978b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -527,8 +527,11 @@ dependencies = [ "lastfm", "log", "parking_lot", + "reqwest", "serde", + "serde_json", "tokio", + "toml", "warp", ] @@ -1030,6 +1033,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1185,9 +1197,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ "backtrace", "bytes", @@ -1260,6 +1272,40 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -1693,6 +1739,15 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "winnow" +version = "0.5.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c830786f7720c2fd27a1a0e27a709dbd3c4d009b56d098fc742d4f4eab91fe2" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" diff --git a/Cargo.toml b/Cargo.toml index d8ddbc2..e29489b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,11 @@ edition = "2021" warp = "0.3.6" parking_lot = "0.12.1" serde = { version = "1.0", features = ["derive"] } -tokio = { version = "1.34", features = ["macros"] } +tokio = { version = "1.35", features = ["macros"] } dotenv = "0.15.0" lastfm = "0.6.1" log = "0.4.20" chrono = "0.4.31" +toml = "0.8.8" +reqwest = { version = "0.11.22", features = ["json"] } +serde_json = "1.0.108" diff --git a/README.md b/README.md index 227b724..69f8ec2 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,36 @@ # jonas_jones-api - api used for my website + +API supporting the Jonas_Jones project infrastructure. + +## Installation + +As of now, the project has no proper production build and unless the proper environment variables are set, the API will not work. + + +Clone the repository and install the dependencies. +```bash +$ git clone git@github.com:J-onasJones/jonas_jones-api.git +$ cd jonas_jones-api +$ cargo build +``` + +## Usage + +To run the API, simply run the following command. +```bash +$ cargo run +``` + +If you want to run the API in a production environment, you will need to set the following environment variables. + +- API_PORT +- API_IP = +- LASTFM_API_KEY +- LASTFM_API_SECRET + +```bash +$ export API_PORT={port} +$ export API_IP={ip_address} +$ export LASTFM_API_KEY={lastfm_api_key} +$ export LASTFM_API_SECRET={lastfm_api_secret} +``` diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..e3bbad0 --- /dev/null +++ b/config.toml @@ -0,0 +1,10 @@ +[server] +host = "localhost" +port = 8080 + +[lastfm] +api_key = "" +api_secret = "" + +[database] +host = "" diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..22e5132 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,84 @@ + +use std::fs::{File, OpenOptions}; +use std::io::prelude::*; +use serde::{Deserialize, Serialize}; + +use crate::Logger; + +#[derive(Debug, Deserialize, Serialize)] +pub struct ServerConfig { + host: String, + port: u32, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct LastFMConfig { + api_key: String, + api_secret: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct DatabaseConfig { + host: String, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct Config { + server: ServerConfig, + lastfm: LastFMConfig, + database: DatabaseConfig, +} + +pub fn load_config() -> (ServerConfig, LastFMConfig, DatabaseConfig) { + // load config.toml. create new one if it doesn't exist + // return config + let config_path = "config.toml"; + if !std::path::Path::new(config_path).exists() { + // If it doesn't exist, create a new config file with default values + create_default_config(config_path); + } + + // Read the configuration file + let mut config_file = File::open("config.toml").expect({Logger::panic("Failed to open config file"); std::process::exit(1)}); + let mut config_toml = String::new(); + config_file + .read_to_string(&mut config_toml) + .expect("Failed to read config file"); + + // Deserialize the TOML into the AppConfig struct + let config: Config = toml::from_str(&config_toml).expect({Logger::panic("Failed to deserialize config file"); std::process::exit(1)}); + + // Return the config + return (config.server, config.lastfm, config.database); +} + +fn create_default_config(path: &str) { + // Create default Config + let default_config = Config { + server: ServerConfig { + host: String::from("localhost"), + port: 8080, + }, + lastfm: LastFMConfig { + api_key: String::from(""), + api_secret: String::from(""), + }, + database: DatabaseConfig { + host: String::from(""), + }, + }; + + // Serialize default config to TOML + let toml_string = toml::to_string_pretty(&default_config).expect("Failed to serialize config"); + + // Write the TOML string to the config file + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(path) + .expect("Failed to open config file for writing"); + + file.write_all(toml_string.as_bytes()) + .expect("Failed to write to config file"); +} \ No newline at end of file diff --git a/src/db/apps/mod.rs b/src/db/apps/mod.rs new file mode 100644 index 0000000..f2bf7e1 --- /dev/null +++ b/src/db/apps/mod.rs @@ -0,0 +1 @@ +pub fn main() {} \ No newline at end of file diff --git a/src/db/mod.rs b/src/db/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/db/users/mod.rs b/src/db/users/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/v1/builtin/mod.rs b/src/v1/builtin/mod.rs index 1a6f902..b0ab25e 100644 --- a/src/v1/builtin/mod.rs +++ b/src/v1/builtin/mod.rs @@ -1,16 +1,5 @@ -// pub fn help() -> warp::reply::Response { - -// return warp::reply::Reply::with_header( -// warp::reply::html(""), -// "Content-Type", -// "text/html", -// ); -// } - -// create a function help() that returns a response with a ststus code of 200 and a body of "help" - - use warp::Filter; + pub fn get_builtin_routes() -> impl warp::Filter + Clone { warp::path("v1") .and((warp::path("help").map(|| "help")) diff --git a/src/v1/kcomebacks/mod.rs b/src/v1/kcomebacks/mod.rs index 96850e3..d921892 100644 --- a/src/v1/kcomebacks/mod.rs +++ b/src/v1/kcomebacks/mod.rs @@ -1,3 +1,59 @@ -pub fn main() { - -} \ No newline at end of file +use std::convert::Infallible; + +use warp::Filter; +use reqwest::Error; + +pub fn get_kcomebacks_routes() -> impl warp::Filter + Clone { + warp::path("v1").and(warp::path("kcomebacks")) + // - /v1/kcomebacks/last_update + // - /v1/kcomebacks/start_update with token + // - /v1/kcomebacks/upcoming/today?limit={0-50}&offset={n} + // - /v1/kcomebacks/upcoming/week?limit={0-50}&offset={n} + // - /v1/kcomebacks/upcoming/month?limit={0-50}&offset={n} + // - /v1/kcomebacks/filter/id?id={n} + // - /v1/kcomebacks/filter/daterange?start={date: YYYY-MM-DD}&end={date: YYYY-MM-DD}&limit={0-50}&offset={n} + // - /v1/kcomebacks/filter/artist?artist={artist}&limit={0-50}&offset={n} + // - /v1/kcomebacks/filter/first + // - /v1/kcomebacks/filter/last + // - /v1/kcomebacks/filter/title?title={title}&limit={0-50}&offset={n} + // - /v1/kcomebacks/filter/type?type={type}&limit={0-50}&offset={n} + // - /v1/kcomebacks/filter/gettypes + + .and(warp::path("last_update").and(warp::get()).and_then(last_update_handler) + .or(warp::path("start_update").map(|| "Not implemented yet"))) +} + +// get json data from https://cdn.jonasjones.dev/api/kcomebacks/rkpop_data.json +async fn fetch_data() -> Result { + let url = "https://cdn.jonasjones.dev/api/kcomebacks/rkpop_data.json"; + let response = reqwest::get(url).await?; + + if response.status().is_success() { + // Parse the JSON response + let json_data: serde_json::Value = response.json().await?; + return Ok(json_data); + } else { + // Handle non-successful status codes + Err(response.error_for_status().unwrap_err()) + } +} + +async fn last_update_handler() -> Result { + + match last_update().await { + Ok(last_update_value) => Ok(warp::reply::json(&last_update_value)), + Err(_) => { + #[derive(Debug)] + struct InternalServerError; + + impl warp::reject::Reject for InternalServerError {} + Err(warp::reject::custom(InternalServerError)) + } + } +} + +async fn last_update() -> Result { + // get the value of last_update of the first element of the json that fetch_data() returns + let last_update_value = fetch_data().await?.get(0).unwrap().get("last_update").unwrap().clone(); + return Ok(last_update_value); +} diff --git a/src/v1/kcomebacks/upcoming/mod.rs b/src/v1/kcomebacks/upcoming/mod.rs new file mode 100644 index 0000000..bde5116 --- /dev/null +++ b/src/v1/kcomebacks/upcoming/mod.rs @@ -0,0 +1,8 @@ + + +pub fn get_kcomebacks_upcoming_routes() -> impl warp::Filter + Clone { + warp::path("upcoming") + .and(warp::path("today").and(warp::get()).and_then(upcoming_today_handler)) + .or(warp::path("week").and(warp::get()).and_then(upcoming_week_handler)) + .or(warp::path("month").and(warp::get()).and_then(upcoming_month_handler)) +} \ No newline at end of file diff --git a/src/v1/mod.rs b/src/v1/mod.rs index ece7cb5..7c33f3e 100644 --- a/src/v1/mod.rs +++ b/src/v1/mod.rs @@ -1,7 +1,12 @@ mod builtin; +mod kcomebacks; pub use builtin::get_builtin_routes as get_v1_builtin_routes; +pub use kcomebacks::get_kcomebacks_routes as get_v1_kcomebacks_routes; + +use warp::Filter; pub fn get_v1_routes() -> impl warp::Filter + Clone { - return get_v1_builtin_routes(); + return get_v1_builtin_routes() + .or(get_v1_kcomebacks_routes()); } \ No newline at end of file