!* Splitting up the admin module
This is primarily for organizational reasons however it should make things easier to track as the complexity increases with features getting filled out.
This commit is contained in:
parent
7c85c656e9
commit
0d9991c557
1
.gitignore
vendored
1
.gitignore
vendored
@ -27,3 +27,4 @@ aws/playbooks/hosts
|
||||
|
||||
ts/dist/
|
||||
ts/node_modules/
|
||||
.vscode/settings.json
|
||||
|
@ -1,95 +0,0 @@
|
||||
#[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<Self, Self::Error> {
|
||||
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<i32,i32> = HashMap::new(); // does not allocate
|
||||
return Template::render("admin", &h);
|
||||
}
|
||||
|
||||
#[post("/dashboard")]
|
||||
pub async fn dashboard(_key: ApiKey) -> Json<ActionResponse> {
|
||||
// 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?<category>&<filename>")]
|
||||
async fn updload_video(_key: ApiKey, category: String, filename: String) -> &'static str {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[delete("/remove-video?<category>&<filename>")]
|
||||
async fn remove_video(_key: ApiKey, category: String, filename: String) -> &'static str {
|
||||
todo!()
|
||||
}
|
||||
|
||||
|
46
api/src/admin/apikey.rs
Normal file
46
api/src/admin/apikey.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use rocket::request::{Outcome, Request, FromRequest};
|
||||
use rocket::async_trait;
|
||||
use rocket::http::Status;
|
||||
|
||||
use crate::db::{self, DB_PATH};
|
||||
|
||||
pub struct ApiKey {
|
||||
uid: String,
|
||||
key: String
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ApiKeyError {
|
||||
Missing,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
|
||||
#[async_trait]
|
||||
impl<'r> FromRequest<'r> for ApiKey {
|
||||
type Error = ApiKeyError;
|
||||
async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
|
||||
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))
|
||||
}
|
||||
}
|
39
api/src/admin/mod.rs
Normal file
39
api/src/admin/mod.rs
Normal file
@ -0,0 +1,39 @@
|
||||
#[cfg(feature = "admin")]
|
||||
|
||||
mod apikey;
|
||||
mod response;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use apikey::ApiKey;
|
||||
use response::ActionResponse;
|
||||
use rocket::serde::json::Json;
|
||||
|
||||
|
||||
|
||||
use rocket_dyn_templates::Template;
|
||||
|
||||
#[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<i32,i32> = HashMap::new(); // does not allocate
|
||||
return Template::render("admin", &h);
|
||||
}
|
||||
|
||||
#[post("/dashboard")]
|
||||
pub async fn dashboard(_key: ApiKey) -> Json<ActionResponse> {
|
||||
// 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?<category>&<filename>")]
|
||||
async fn updload_video(_key: ApiKey, category: String, filename: String) -> &'static str {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[delete("/remove-video?<category>&<filename>")]
|
||||
async fn remove_video(_key: ApiKey, category: String, filename: String) -> &'static str {
|
||||
todo!()
|
||||
}
|
4
api/src/admin/response.rs
Normal file
4
api/src/admin/response.rs
Normal file
@ -0,0 +1,4 @@
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ActionResponse(pub &'static str);
|
Loading…
Reference in New Issue
Block a user