From 726b824620b23696458d8a215ca2f2126d0b7f67 Mon Sep 17 00:00:00 2001 From: Mike Dilger Date: Thu, 27 Jun 2024 09:54:15 +1200 Subject: [PATCH] Clean up --- src/error.rs | 15 +++- src/web/management/mod.rs | 173 ++++++++++++++++++++------------------ 2 files changed, 104 insertions(+), 84 deletions(-) diff --git a/src/error.rs b/src/error.rs index a51e8da..26a5cef 100644 --- a/src/error.rs +++ b/src/error.rs @@ -29,7 +29,7 @@ pub enum ChorusError { AuthRequired, // Bad request - BadRequest(String), + BadRequest(&'static str), // Event is banned BannedEvent, @@ -85,6 +85,9 @@ pub enum ChorusError { // No private key NoPrivateKey, + // Not Implemented + NotImplemented, + // No such subscription NoSuchSubscription, @@ -161,6 +164,7 @@ impl std::fmt::Display for ChorusError { ChorusError::ManagementAuthFailure(s) => write!(f, "Authorization failure: {s}"), ChorusError::MissingTable(t) => write!(f, "Missing table: {t}"), ChorusError::NoPrivateKey => write!(f, "Private Key Not Found"), + ChorusError::NotImplemented => write!(f, "Not implemented"), ChorusError::NoSuchSubscription => write!(f, "No such subscription"), ChorusError::PocketDb(e) => write!(f, "{e}"), ChorusError::PocketDbHeed(e) => write!(f, "{e}"), @@ -209,6 +213,14 @@ impl StdError for ChorusError { } impl ChorusError { + #[track_caller] + pub fn into_err(self) -> Error { + Error { + inner: self, + location: std::panic::Location::caller(), + } + } + pub fn punishment(&self) -> f32 { match self { ChorusError::AuthFailure(_) => 0.25, @@ -232,6 +244,7 @@ impl ChorusError { ChorusError::ManagementAuthFailure(_) => 0.0, ChorusError::MissingTable(_) => 0.0, ChorusError::NoPrivateKey => 0.0, + ChorusError::NotImplemented => 0.0, ChorusError::NoSuchSubscription => 0.05, ChorusError::PocketDb(_) => 0.0, ChorusError::PocketDbHeed(_) => 0.0, diff --git a/src/web/management/mod.rs b/src/web/management/mod.rs index 4e92ea7..701481a 100644 --- a/src/web/management/mod.rs +++ b/src/web/management/mod.rs @@ -8,6 +8,18 @@ use pocket_types::Pubkey; use serde_json::{json, Map, Value}; mod auth; +fn respond(json: serde_json::Value, status: StatusCode) -> Result>, Error> { + let s: String = serde_json::to_string(&json)?; + let response = Response::builder() + .header("Access-Control-Allow-Origin", "*") + .header("Access-Control-Allow-Headers", "*") + .header("Access-Control-Allow-Methods", "*") + .header("Content-Type", "application/nostr+json") + .status(status) + .body(s.into_bytes().into())?; + Ok(response) +} + pub async fn handle( _peer: HashedPeer, request: Request, @@ -23,120 +35,115 @@ pub async fn handle( } }; + match handle_inner(command) { + Ok(Some(value)) => respond(value, StatusCode::OK), + Ok(None) => { + let result = json!({ + "result": {}, + }); + respond(result, StatusCode::OK) + } + Err(e) => { + let (result, status) = match e.inner { + ChorusError::BadRequest(s) => ( + json!({ + "result": {}, + "error": format!("{}", s) + }), + StatusCode::BAD_REQUEST, + ), + ChorusError::NotImplemented => ( + json!({ + "result": {}, + "error": "not_implemented" + }), + StatusCode::NOT_IMPLEMENTED, + ), + _ => ( + json!({ + "result": {}, + "error": format!("{}", e) + }), + StatusCode::INTERNAL_SERVER_ERROR, + ), + }; + return respond(result, status); + } + } +} + +pub fn handle_inner(command: Value) -> Result, Error> { let obj = match command.as_object() { Some(o) => o, - None => return fail("Command was not a JSON object"), + None => return Err(ChorusError::BadRequest("Command was not a JSON object").into()), }; let method = match obj.get("method") { Some(m) => match m.as_str() { Some(s) => s.to_owned(), - None => return fail("Method not a string"), + None => return Err(ChorusError::BadRequest("Method not a string").into()), }, - None => return fail("Method missing"), + None => return Err(ChorusError::BadRequest("Method missing").into()), }; match &*method { "supportedmethods" => { - return respond( - json!({ - "result": ["allowpubkey", "banpubkey", "supportedmethods"] - }), - StatusCode::OK, - ) + return Ok(Some(json!({ + "result": ["allowpubkey", "banpubkey", "supportedmethods"] + }))); } // Pubkeys - "banpubkey" => match get_pubkey_param(obj) { - Ok(pk) => { - crate::mark_pubkey_approval(GLOBALS.store.get().unwrap(), pk, false)?; - return worked(); - } - Err(e) => return fail(&format!("{e}")), - }, - "allowpubkey" => match get_pubkey_param(obj) { - Ok(pk) => { - crate::mark_pubkey_approval(GLOBALS.store.get().unwrap(), pk, true)?; - return worked(); - } - Err(e) => return fail(&format!("{e}")), - }, - "listbannedpubkeys" => return fail(&format!("Unsupported method {}", method)), - "listallowedpubkeys" => return fail(&format!("Unsupported method {}", method)), + "banpubkey" => { + let pk = get_pubkey_param(obj)?; + crate::mark_pubkey_approval(GLOBALS.store.get().unwrap(), pk, false)?; + return Ok(None); + } + "allowpubkey" => { + let pk = get_pubkey_param(obj)?; + crate::mark_pubkey_approval(GLOBALS.store.get().unwrap(), pk, true)?; + return Ok(None); + } + "listbannedpubkeys" => return Err(ChorusError::NotImplemented.into()), + "listallowedpubkeys" => return Err(ChorusError::NotImplemented.into()), // Events - "banevent" => return fail(&format!("Unsupported method {}", method)), - "listbannedevents" => return fail(&format!("Unsupported method {}", method)), - "allowevent" => return fail(&format!("Unsupported method {}", method)), - "listallowedevents" => return fail(&format!("Unsupported method {}", method)), - "listeventsneedingmoderation" => return fail(&format!("Unsupported method {}", method)), + "banevent" => return Err(ChorusError::NotImplemented.into()), + "listbannedevents" => return Err(ChorusError::NotImplemented.into()), + "allowevent" => return Err(ChorusError::NotImplemented.into()), + "listallowedevents" => return Err(ChorusError::NotImplemented.into()), + "listeventsneedingmoderation" => return Err(ChorusError::NotImplemented.into()), // Kinds - "allowkind" => return fail(&format!("Unsupported method {}", method)), - "disallowkind" => return fail(&format!("Unsupported method {}", method)), - "listbannedkinds" => return fail(&format!("Unsupported method {}", method)), - "listallowedkinds" => return fail(&format!("Unsupported method {}", method)), + "allowkind" => return Err(ChorusError::NotImplemented.into()), + "disallowkind" => return Err(ChorusError::NotImplemented.into()), + "listbannedkinds" => return Err(ChorusError::NotImplemented.into()), + "listallowedkinds" => return Err(ChorusError::NotImplemented.into()), // IP addresses - "blockip" => return fail(&format!("Unsupported method {}", method)), - "unblockip" => return fail(&format!("Unsupported method {}", method)), - "listblockedips" => return fail(&format!("Unsupported method {}", method)), + "blockip" => return Err(ChorusError::NotImplemented.into()), + "unblockip" => return Err(ChorusError::NotImplemented.into()), + "listblockedips" => return Err(ChorusError::NotImplemented.into()), // Config - "changerelayname" => return fail(&format!("Unsupported method {}", method)), - "changerelaydescription" => return fail(&format!("Unsupported method {}", method)), - "changerelayicon" => return fail(&format!("Unsupported method {}", method)), + "changerelayname" => return Err(ChorusError::NotImplemented.into()), + "changerelaydescription" => return Err(ChorusError::NotImplemented.into()), + "changerelayicon" => return Err(ChorusError::NotImplemented.into()), - _ => return fail(&format!("Unsupported method {}", method)), + _ => return Err(ChorusError::NotImplemented.into()), } - - /* - let result = json!({ - "result": {}, - "error": "The Management API is not yet implemented" - }); - respond(result, StatusCode::NOT_IMPLEMENTED) - */ -} - -fn fail(msg: &str) -> Result>, Error> { - let result = json!({ - "result": {}, - "error": msg - }); - respond(result, StatusCode::BAD_REQUEST) -} - -fn worked() -> Result>, Error> { - let result = json!({ - "result": {}, - }); - respond(result, StatusCode::OK) -} - -fn respond(json: serde_json::Value, status: StatusCode) -> Result>, Error> { - let s: String = serde_json::to_string(&json)?; - let response = Response::builder() - .header("Access-Control-Allow-Origin", "*") - .header("Access-Control-Allow-Headers", "*") - .header("Access-Control-Allow-Methods", "*") - .header("Content-Type", "application/nostr+json") - .status(status) - .body(s.into_bytes().into())?; - Ok(response) } fn get_pubkey_param(obj: &Map) -> Result { let pubkey_text = obj .get("params") - .ok_or::(ChorusError::BadRequest("Params field missing".to_owned()).into())? + .ok_or(ChorusError::BadRequest("Params field missing").into_err())? .as_array() - .ok_or::(ChorusError::BadRequest("Params not an array".to_owned()).into())? + .ok_or(ChorusError::BadRequest("Params not an array").into_err())? .get(0) - .ok_or::(ChorusError::BadRequest("Missing pubkey parameter".to_owned()).into())? + .ok_or(ChorusError::BadRequest("Missing pubkey parameter").into_err())? .as_str() - .ok_or::( - ChorusError::BadRequest("Pubkey parameter is wrong type".to_owned()).into(), - )?; - Ok(Pubkey::read_hex(pubkey_text.as_bytes())?) + .ok_or(ChorusError::BadRequest("Pubkey parameter is wrong type").into_err())?; + Ok(Pubkey::read_hex(pubkey_text.as_bytes()) + .map_err(|_| ChorusError::BadRequest("Pubkey could not be parsed").into_err())?) }