This commit is contained in:
Mike Dilger 2024-06-27 09:54:15 +12:00
parent 8d36ca03b7
commit 726b824620
2 changed files with 104 additions and 84 deletions

View File

@ -29,7 +29,7 @@ pub enum ChorusError {
AuthRequired, AuthRequired,
// Bad request // Bad request
BadRequest(String), BadRequest(&'static str),
// Event is banned // Event is banned
BannedEvent, BannedEvent,
@ -85,6 +85,9 @@ pub enum ChorusError {
// No private key // No private key
NoPrivateKey, NoPrivateKey,
// Not Implemented
NotImplemented,
// No such subscription // No such subscription
NoSuchSubscription, NoSuchSubscription,
@ -161,6 +164,7 @@ impl std::fmt::Display for ChorusError {
ChorusError::ManagementAuthFailure(s) => write!(f, "Authorization failure: {s}"), ChorusError::ManagementAuthFailure(s) => write!(f, "Authorization failure: {s}"),
ChorusError::MissingTable(t) => write!(f, "Missing table: {t}"), ChorusError::MissingTable(t) => write!(f, "Missing table: {t}"),
ChorusError::NoPrivateKey => write!(f, "Private Key Not Found"), ChorusError::NoPrivateKey => write!(f, "Private Key Not Found"),
ChorusError::NotImplemented => write!(f, "Not implemented"),
ChorusError::NoSuchSubscription => write!(f, "No such subscription"), ChorusError::NoSuchSubscription => write!(f, "No such subscription"),
ChorusError::PocketDb(e) => write!(f, "{e}"), ChorusError::PocketDb(e) => write!(f, "{e}"),
ChorusError::PocketDbHeed(e) => write!(f, "{e}"), ChorusError::PocketDbHeed(e) => write!(f, "{e}"),
@ -209,6 +213,14 @@ impl StdError for ChorusError {
} }
impl ChorusError { impl ChorusError {
#[track_caller]
pub fn into_err(self) -> Error {
Error {
inner: self,
location: std::panic::Location::caller(),
}
}
pub fn punishment(&self) -> f32 { pub fn punishment(&self) -> f32 {
match self { match self {
ChorusError::AuthFailure(_) => 0.25, ChorusError::AuthFailure(_) => 0.25,
@ -232,6 +244,7 @@ impl ChorusError {
ChorusError::ManagementAuthFailure(_) => 0.0, ChorusError::ManagementAuthFailure(_) => 0.0,
ChorusError::MissingTable(_) => 0.0, ChorusError::MissingTable(_) => 0.0,
ChorusError::NoPrivateKey => 0.0, ChorusError::NoPrivateKey => 0.0,
ChorusError::NotImplemented => 0.0,
ChorusError::NoSuchSubscription => 0.05, ChorusError::NoSuchSubscription => 0.05,
ChorusError::PocketDb(_) => 0.0, ChorusError::PocketDb(_) => 0.0,
ChorusError::PocketDbHeed(_) => 0.0, ChorusError::PocketDbHeed(_) => 0.0,

View File

@ -8,6 +8,18 @@ use pocket_types::Pubkey;
use serde_json::{json, Map, Value}; use serde_json::{json, Map, Value};
mod auth; mod auth;
fn respond(json: serde_json::Value, status: StatusCode) -> Result<Response<Full<Bytes>>, 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( pub async fn handle(
_peer: HashedPeer, _peer: HashedPeer,
request: Request<Incoming>, request: Request<Incoming>,
@ -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<Option<Value>, Error> {
let obj = match command.as_object() { let obj = match command.as_object() {
Some(o) => o, 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") { let method = match obj.get("method") {
Some(m) => match m.as_str() { Some(m) => match m.as_str() {
Some(s) => s.to_owned(), 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 { match &*method {
"supportedmethods" => { "supportedmethods" => {
return respond( return Ok(Some(json!({
json!({ "result": ["allowpubkey", "banpubkey", "supportedmethods"]
"result": ["allowpubkey", "banpubkey", "supportedmethods"] })));
}),
StatusCode::OK,
)
} }
// Pubkeys // Pubkeys
"banpubkey" => match get_pubkey_param(obj) { "banpubkey" => {
Ok(pk) => { let pk = get_pubkey_param(obj)?;
crate::mark_pubkey_approval(GLOBALS.store.get().unwrap(), pk, false)?; crate::mark_pubkey_approval(GLOBALS.store.get().unwrap(), pk, false)?;
return worked(); return Ok(None);
} }
Err(e) => return fail(&format!("{e}")), "allowpubkey" => {
}, let pk = get_pubkey_param(obj)?;
"allowpubkey" => match get_pubkey_param(obj) { crate::mark_pubkey_approval(GLOBALS.store.get().unwrap(), pk, true)?;
Ok(pk) => { return Ok(None);
crate::mark_pubkey_approval(GLOBALS.store.get().unwrap(), pk, true)?; }
return worked(); "listbannedpubkeys" => return Err(ChorusError::NotImplemented.into()),
} "listallowedpubkeys" => return Err(ChorusError::NotImplemented.into()),
Err(e) => return fail(&format!("{e}")),
},
"listbannedpubkeys" => return fail(&format!("Unsupported method {}", method)),
"listallowedpubkeys" => return fail(&format!("Unsupported method {}", method)),
// Events // Events
"banevent" => return fail(&format!("Unsupported method {}", method)), "banevent" => return Err(ChorusError::NotImplemented.into()),
"listbannedevents" => return fail(&format!("Unsupported method {}", method)), "listbannedevents" => return Err(ChorusError::NotImplemented.into()),
"allowevent" => return fail(&format!("Unsupported method {}", method)), "allowevent" => return Err(ChorusError::NotImplemented.into()),
"listallowedevents" => return fail(&format!("Unsupported method {}", method)), "listallowedevents" => return Err(ChorusError::NotImplemented.into()),
"listeventsneedingmoderation" => return fail(&format!("Unsupported method {}", method)), "listeventsneedingmoderation" => return Err(ChorusError::NotImplemented.into()),
// Kinds // Kinds
"allowkind" => return fail(&format!("Unsupported method {}", method)), "allowkind" => return Err(ChorusError::NotImplemented.into()),
"disallowkind" => return fail(&format!("Unsupported method {}", method)), "disallowkind" => return Err(ChorusError::NotImplemented.into()),
"listbannedkinds" => return fail(&format!("Unsupported method {}", method)), "listbannedkinds" => return Err(ChorusError::NotImplemented.into()),
"listallowedkinds" => return fail(&format!("Unsupported method {}", method)), "listallowedkinds" => return Err(ChorusError::NotImplemented.into()),
// IP addresses // IP addresses
"blockip" => return fail(&format!("Unsupported method {}", method)), "blockip" => return Err(ChorusError::NotImplemented.into()),
"unblockip" => return fail(&format!("Unsupported method {}", method)), "unblockip" => return Err(ChorusError::NotImplemented.into()),
"listblockedips" => return fail(&format!("Unsupported method {}", method)), "listblockedips" => return Err(ChorusError::NotImplemented.into()),
// Config // Config
"changerelayname" => return fail(&format!("Unsupported method {}", method)), "changerelayname" => return Err(ChorusError::NotImplemented.into()),
"changerelaydescription" => return fail(&format!("Unsupported method {}", method)), "changerelaydescription" => return Err(ChorusError::NotImplemented.into()),
"changerelayicon" => return fail(&format!("Unsupported method {}", method)), "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<Response<Full<Bytes>>, Error> {
let result = json!({
"result": {},
"error": msg
});
respond(result, StatusCode::BAD_REQUEST)
}
fn worked() -> Result<Response<Full<Bytes>>, Error> {
let result = json!({
"result": {},
});
respond(result, StatusCode::OK)
}
fn respond(json: serde_json::Value, status: StatusCode) -> Result<Response<Full<Bytes>>, 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<String, Value>) -> Result<Pubkey, Error> { fn get_pubkey_param(obj: &Map<String, Value>) -> Result<Pubkey, Error> {
let pubkey_text = obj let pubkey_text = obj
.get("params") .get("params")
.ok_or::<Error>(ChorusError::BadRequest("Params field missing".to_owned()).into())? .ok_or(ChorusError::BadRequest("Params field missing").into_err())?
.as_array() .as_array()
.ok_or::<Error>(ChorusError::BadRequest("Params not an array".to_owned()).into())? .ok_or(ChorusError::BadRequest("Params not an array").into_err())?
.get(0) .get(0)
.ok_or::<Error>(ChorusError::BadRequest("Missing pubkey parameter".to_owned()).into())? .ok_or(ChorusError::BadRequest("Missing pubkey parameter").into_err())?
.as_str() .as_str()
.ok_or::<Error>( .ok_or(ChorusError::BadRequest("Pubkey parameter is wrong type").into_err())?;
ChorusError::BadRequest("Pubkey parameter is wrong type".to_owned()).into(), Ok(Pubkey::read_hex(pubkey_text.as_bytes())
)?; .map_err(|_| ChorusError::BadRequest("Pubkey could not be parsed").into_err())?)
Ok(Pubkey::read_hex(pubkey_text.as_bytes())?)
} }