diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9c2ec6b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +insert_final_newline = true +trim_trailing_whitespace = true + +[*.rs] +indent_style = space +indent_size = 4 diff --git a/.gitignore b/.gitignore index 99a1d79..0cf9fe6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,6 @@ .vscode .idea + +# API related stuff +.api diff --git a/Cargo.lock b/Cargo.lock index d37a210..1a19971 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,7 +87,6 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" dependencies = [ - "actix-macros", "futures-core", "tokio", ] @@ -283,9 +282,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bcrypt" @@ -672,6 +671,17 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "futures-sink" version = "0.3.30" @@ -692,6 +702,7 @@ checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-core", "futures-io", + "futures-macro", "futures-task", "memchr", "pin-project-lite", @@ -711,9 +722,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "js-sys", @@ -730,9 +741,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" dependencies = [ "bytes", "fnv", @@ -759,6 +770,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hmac" version = "0.12.1" @@ -931,9 +948,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] @@ -1575,6 +1592,19 @@ dependencies = [ "digest", ] +[[package]] +name = "sha256" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18278f6a914fa3070aa316493f7d2ddfb9ac86ebc06fa3b83bffda487e9065b0" +dependencies = [ + "async-trait", + "bytes", + "hex", + "sha2", + "tokio", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1691,9 +1721,9 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] @@ -1930,16 +1960,16 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" name = "vybr-api" version = "0.0.1" dependencies = [ - "actix-rt", - "actix-service", "actix-web", "async-trait", + "base64", "bcrypt", "chrono", "diesel", "diesel_migrations", "dotenvy", "env_logger", + "futures-util", "hmac", "jsonwebtoken", "lazy_static", @@ -1947,7 +1977,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "sha2", + "sha256", ] [[package]] @@ -1967,9 +1997,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1977,9 +2007,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", @@ -1992,9 +2022,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" dependencies = [ "cfg-if", "js-sys", @@ -2004,9 +2034,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2014,9 +2044,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", @@ -2027,15 +2057,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" dependencies = [ "js-sys", "wasm-bindgen", @@ -2215,9 +2245,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.33" +version = "0.5.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7520bbdec7211caa7c4e682eb1fbe07abe20cee6756b6e00f537c82c11816aa" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 920e9a5..c3a7ddb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,21 +6,21 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -actix-rt = "2" actix-web = "4" async-trait = "0.1" +base64 = "0.21.5" bcrypt = "0.15" chrono = { version = "0.4", features = ["serde"] } diesel = { version = "2", features = ["r2d2", "postgres", "chrono"] } diesel_migrations = "2" dotenvy = "*" env_logger = "0.10" +futures-util = "0.3" hmac = "0.12" log = "0.4" reqwest = { version = "0.11", features = ["blocking", "json"] } -sha2 = "0.10" +sha256 = "1.5.0" lazy_static = "1" jsonwebtoken = "9" serde = "1" serde_json = "1" -actix-service = "2.0.2" diff --git a/src/main.rs b/src/main.rs index d3f1dc8..eda73ee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,6 +24,7 @@ async fn main() -> std::io::Result<()> { HttpServer::new(move || { App::new() + .wrap(middlewares::cache::Caching) .service(routes::auth::routes()) // .wrap(middlewares::auth::auth()) .service(routes::playlists::routes()) diff --git a/src/middlewares/cache.rs b/src/middlewares/cache.rs new file mode 100644 index 0000000..eb2c3c2 --- /dev/null +++ b/src/middlewares/cache.rs @@ -0,0 +1,145 @@ +use base64::{engine::general_purpose, Engine as _}; +use std::{ + fs::File, + future::{ready, Ready}, + io::Write, +}; + +use actix_web::{ + body::MessageBody, + dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, + http::{ + self, + header::{HeaderName, HeaderValue}, + }, + web::Bytes, + Error, HttpResponse, +}; +use serde::{Deserialize, Serialize}; +use sha256::digest; + +use futures_util::future::LocalBoxFuture; + +#[derive(Serialize, Deserialize)] +struct ResponseCache { + data: ResponseCacheData, + revalidate: u32, + tags: Vec, + url: String, +} + +#[derive(Serialize, Deserialize)] +struct ResponseCacheData { + body: String, + status: u16, +} + +pub struct Caching; + +impl Transform for Caching +where + S: Service, Error = Error>, + S::Future: 'static, + B: MessageBody + std::fmt::Debug, +{ + type Response = ServiceResponse; + type Error = Error; + type InitError = (); + type Transform = CachingMiddleware; + type Future = Ready>; + + fn new_transform(&self, service: S) -> Self::Future { + ready(Ok(CachingMiddleware { service })) + } +} + +pub struct CachingMiddleware { + service: S, +} + +// impl CachingMiddlwareService { +// fn get_cache_file(url: &str) -> File {} +// } + +impl Service for CachingMiddleware +where + S: Service, Error = Error>, + S::Future: 'static, + B: MessageBody + std::fmt::Debug, +{ + type Response = ServiceResponse; + type Error = Error; + type Future = LocalBoxFuture<'static, Result>; + + forward_ready!(service); + + fn call(&self, req: ServiceRequest) -> Self::Future { + let url = digest(req.uri().to_string()); + + // check if cache file exists + let cache_file = File::open(format!(".api/cache/{url}.json")); + + // if it does, check if it's valid + if let Ok(file) = cache_file { + let cache: ResponseCache = serde_json::from_reader(file).unwrap(); + + // if it's valid, return it + // if cache.revalidate > 0 { + let body = general_purpose::STANDARD_NO_PAD.decode(cache.data.body.as_bytes()); + + match body { + Ok(body) => { + let mut res = + HttpResponse::new(http::StatusCode::from_u16(cache.data.status).unwrap()) + .set_body(Bytes::from(body)); + + res.headers_mut().insert( + HeaderName::from_static("x-vybr-api"), + HeaderValue::from_static("Hello"), + ); + + res.headers_mut().insert( + HeaderName::from_static("content-type"), + HeaderValue::from_static("application/json"), + ); + + return Box::pin(async move { + let res = ServiceResponse::new(req.request().clone(), res); + Ok(res) + }); + } + Err(err) => println!("Error decoding body: {:?}", err), + } + } + + // otherwise continue. + let fut = self.service.call(req); + + Box::pin(async move { + let res = fut.await?; + + let (req, res) = res.into_parts(); + let (res, body) = res.into_parts(); + + let body_bytes = body.try_into_bytes().unwrap(); + + let content = general_purpose::STANDARD_NO_PAD.encode(&body_bytes); + let file_content = serde_json::to_string(&ResponseCache { + data: ResponseCacheData { + body: content, + status: res.status().as_u16(), + }, + revalidate: 0, + tags: vec![], + url: req.uri().to_string(), + }) + .unwrap(); + + let mut file = File::create(format!(".api/cache/{url}.json")).unwrap(); + file.write_all(file_content.as_bytes()).unwrap(); + + let res = res.set_body(body_bytes); + Ok(ServiceResponse::new(req, res)) + }) + } +} diff --git a/src/middlewares/mod.rs b/src/middlewares/mod.rs index 0616f91..89cf86f 100644 --- a/src/middlewares/mod.rs +++ b/src/middlewares/mod.rs @@ -1,2 +1,4 @@ pub mod error; pub mod user; + +pub mod cache; diff --git a/src/models/playlists.rs b/src/models/playlists.rs index bdb05af..5911eec 100644 --- a/src/models/playlists.rs +++ b/src/models/playlists.rs @@ -23,9 +23,9 @@ pub struct Playlists { pub public: bool, pub playlist_type: String, - pub parent_id: Option, pub creator_id: String, + pub parent_id: Option, pub created_at: Option, pub updated_at: Option, diff --git a/src/schema.rs b/src/schema.rs index ebc6b6b..875aac3 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -32,12 +32,11 @@ diesel::table! { #[max_length = 255] name -> Varchar, public -> Bool, - #[max_length = 24] playlist_type -> Varchar, #[max_length = 24] - parent_id -> Nullable, - #[max_length = 24] creator_id -> Varchar, + #[max_length = 24] + parent_id -> Nullable, created_at -> Nullable, updated_at -> Nullable, }