diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..6b53e44 --- /dev/null +++ b/backend/.env @@ -0,0 +1,2 @@ +DB_PATH=./db.sqlite +API_KEY=p123 diff --git a/backend/.gitignore b/backend/.gitignore index ea8c4bf..4648120 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1 +1,25 @@ -/target +.env +*.sqlite + +### Rust template +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +#Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# RustRover +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ diff --git a/backend/Cargo.lock b/backend/Cargo.lock index 5490b96..8f6fb34 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -87,6 +87,8 @@ dependencies = [ "dotenv", "rocket", "rusqlite", + "serde", + "serde_json", ] [[package]] @@ -960,6 +962,7 @@ dependencies = [ "rocket_codegen", "rocket_http", "serde", + "serde_json", "state", "tempfile", "time", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index b0fa34a..4202620 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2024" [dependencies] -rocket = "0.5.1" +rocket = { version = "0.5.0-rc.2", features = ["json"] } dotenv = "0.15.0" rusqlite = { version = "0.35.0", features = ["bundled"] } +serde = { version = "1.0.147", features = ["derive"] } +serde_json = "1.0.88" diff --git a/backend/src/auth.rs b/backend/src/auth.rs new file mode 100644 index 0000000..bb25561 --- /dev/null +++ b/backend/src/auth.rs @@ -0,0 +1,56 @@ +use rocket::Response; +use rocket::http::Status; +use rocket::request::{FromRequest, Outcome, Request}; +use serde::Serialize; +use serde_json::json; +use std::io::Cursor; + +pub struct ApiKey {} + +#[derive(Serialize)] +pub struct GenericResponse { + pub message: String, +} + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for ApiKey { + type Error = Response<'r>; + + async fn from_request(req: &'r Request<'_>) -> Outcome> { + fn is_valid(key: &str) -> bool { + key == dotenv::var("API_KEY").unwrap() + } + + match req.headers().get_one("Authorization") { + None => { + let body = json!(GenericResponse { + message: "auth token not found".to_string() + }) + .to_string(); + + Outcome::Error(( + Status::Unauthorized, + Response::build() + .status(Status::Unauthorized) + .sized_body(body.len(), Cursor::new(body)) + .finalize(), + )) + } + Some(key) if is_valid(key) => Outcome::Success(ApiKey {}), + Some(_) => { + let body = json!(GenericResponse { + message: "invalid auth token".to_string() + }) + .to_string(); + + Outcome::Error(( + Status::Unauthorized, + Response::build() + .status(Status::Unauthorized) + .sized_body(body.len(), Cursor::new(body)) + .finalize(), + )) + } + } + } +} diff --git a/backend/src/main.rs b/backend/src/main.rs index f219dd6..11498e2 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -1,6 +1,8 @@ mod db; +mod auth; use dotenv; +use auth::ApiKey; #[macro_use] extern crate rocket; @@ -10,11 +12,17 @@ async fn index() -> &'static str { "Hello World!" } +#[get("/hi")] +async fn hello(api_key: ApiKey) -> &'static str { + "Hi!" +} + #[launch] fn rocket() -> _ { dotenv::dotenv().ok(); let db_path = dotenv::var("DB_PATH").expect("DB_PATH is not set"); + dotenv::var("API_KEY").expect("API_KEY is not set"); let db = db::Conn::new(&db_path); - rocket::build().mount("/", routes![index]) + rocket::build().mount("/", routes![index, hello]) }