#[cfg(feature = "admin")] // This module deals with all the routes which are protected by an api key // Without a proper api key sent to the server these routes will // respond with a 401 UNAUTHORIZED response // Below is the implementation required to have custom api keys which // allow people to actually use the service use std::env; use std::collections::HashMap; use rocket::serde::Serialize; use rocket::serde::json::Json; use rocket_dyn_templates::Template; use rocket::request::{self, Outcome, Request, FromRequest}; use rocket::http::Status; use crate::db; use crate::page; lazy_static! { static ref DB_PATH: String = { env::var("DB_PATH").unwrap_or("keys.db".into()) }; } #[derive(Serialize)] struct ActionResponse(&'static str); pub struct ApiKey { uid: String, key: String } #[derive(Debug)] pub enum ApiKeyError { Missing, Invalid, } #[rocket::async_trait] impl<'r> FromRequest<'r> for ApiKey { type Error = ApiKeyError; async fn from_request(req: &'r Request<'_>) -> Outcome { let key = req.headers().get_one("ADMIN-API-KEY"); let uid = req.headers().get_one("ADMIN-API-UID"); if key.is_none() || uid.is_none() { return Outcome::Failure((Status::Forbidden, ApiKeyError::Missing)); } let (key, uid) = (key.unwrap(), uid.unwrap()); println!("Path to use for db file {:?}", DB_PATH.to_string()); let db = db::Database::load(DB_PATH.as_str().into()).unwrap(); if let Some(stored) = db.get(uid) { if stored == key { return Outcome::Success(ApiKey { key: key.into(), uid: uid.into() }) } return Outcome::Failure((Status::Forbidden, ApiKeyError::Invalid)) } return Outcome::Failure((Status::Forbidden, ApiKeyError::Invalid)) } } #[get("/dashboard")] pub async fn login_dashboard() -> Template { // This page is basically just a login form // However the rest of the form is present on this page, just hidden let h: HashMap = HashMap::new(); // does not allocate return Template::render("admin", &h); } #[post("/dashboard")] pub async fn dashboard(_key: ApiKey) -> Json { // API Key auth'd for us so we don't need to bother checking, // this just serves to confirm the credentials are correct Json(ActionResponse("ok")) } #[post("/upload-video?&")] async fn updload_video(_key: ApiKey, category: String, filename: String) -> &'static str { todo!() } #[delete("/remove-video?&")] async fn remove_video(_key: ApiKey, category: String, filename: String) -> &'static str { todo!() }