diff --git a/bin/server/src/api.rs b/bin/server/src/api.rs index be311d8..3a9a898 100644 --- a/bin/server/src/api.rs +++ b/bin/server/src/api.rs @@ -29,6 +29,46 @@ macro_rules! fuck { }; } +// Makes a response. +macro_rules! respond { + ($($arg:tt)*) => { + crate::api::Resp { + $($arg)*, + .. crate::api::Resp::default() + }.into() + }; +} + +/// Parameters for a response +struct Resp<'s> { + body: Option<&'s Value>, + kind: &'s str, + code: u16, +} + +impl<'s> Default for Resp<'s> { + fn default() -> Self { + Resp { + body: None, + kind: "application/json", + code: 200, + } + } +} + +impl<'a> From> for Response { + fn from(Resp { body, kind, code }: Resp<'_>) -> Response { + let resp = Response::<()>::builder() + .status(code) + .header("content-type", kind); + resp.body(match body { + Some(data) => Full::new(serde_json::to_vec(&data).unwrap().into()), + None => Full::new(Bytes::default()), + }) + .unwrap() + } +} + pub mod ap; pub mod wf; @@ -205,23 +245,6 @@ fn with_json( } } -/// A quick, simple way to construct a response. -fn respond( - status: u16, - body: Option>, - headers: [(&str, &str); N], -) -> Response { - let mut resp = Response::<()>::builder().status(status); - for (name, data) in headers { - resp = resp.header(name, data); - } - resp.body(match body { - Some(bytes) => Full::new(bytes.into()), - None => Full::new(Bytes::default()), - }) - .unwrap() -} - mod error { //! Pre-baked error responses. @@ -261,10 +284,13 @@ mod error { } /// Check whether the requester wants json from us. pub fn accepts_json(&self) -> bool { + fn is_json((k, v): (&str, &str)) -> bool { + k == "application" && v.split('+').any(|p| p == "json") + } self.accept .iter() .filter_map(|s| s.split_once('/')) - .any(|(k, v)| k == "application" && v.split('+').any(|p| p == "json")) + .any(is_json) } } } diff --git a/bin/server/src/api/ap.rs b/bin/server/src/api/ap.rs index cf00d6a..913c43b 100644 --- a/bin/server/src/api/ap.rs +++ b/bin/server/src/api/ap.rs @@ -6,19 +6,19 @@ use puppy::{ actor::{get_signing_key, Actor}, get_local_ap_object, Context, Error, Key, }; -use serde_json::{to_string, Value}; +use serde_json::Value; use crate::sig::{Signer, Verifier}; -use super::{error::Message, respond, Response}; +use super::{error::Message, Response}; /// Proxy a request through the instance. pub async fn proxy(cx: &Context, params: &[(&str, &str)]) -> Result { // Extract our query parameters. let Some(user) = params.iter().find_map(|(k, v)| (*k == "user").then_some(v)) else { - fuck!(400: "Expected `user` query param"); + fuck!(400: "expected `user` query param"); }; let Some(url) = params.iter().find_map(|(k, v)| (*k == "url").then_some(v)) else { - fuck!(400: "Expected `url` query param"); + fuck!(400: "expected `url` query param"); }; // Look up the actor's key in the store (which is accessible through the puppy context). @@ -48,19 +48,22 @@ pub fn serve_object(cx: &Context, object_ulid: &str) -> Result() else { fuck!(400: "improperly formatted ulid"); }; + let result = cx.run(|tx| get_local_ap_object(&tx, parsed)); let Ok(object) = result else { fuck!(404: "object does not exist"); }; - let json = to_string(&object.to_json_ld()).unwrap(); - Ok(respond(200, Some(json), [AP_CONTENT_TYPE])) -} -const AP_CONTENT_TYPE: (&str, &str) = ("content-type", "application/activity+json"); + Ok(respond! { + kind: "application/activity+json", + body: Some(&object.to_json_ld()) + }) +} /// Serve the special actor used for signing requests. pub fn serve_verifier_actor(verifier: &Verifier) -> Response { - let body = verifier.to_json_ld(); - let encoded = serde_json::to_vec(&body).unwrap(); - respond(200, Some(encoded), [AP_CONTENT_TYPE]) + respond! { + kind: "application/activity+json", + body: Some(&verifier.to_json_ld()) + } } diff --git a/bin/server/src/api/wf.rs b/bin/server/src/api/wf.rs index 3076399..e27157e 100644 --- a/bin/server/src/api/wf.rs +++ b/bin/server/src/api/wf.rs @@ -7,7 +7,7 @@ use puppy::{ use serde_json::{json, Value}; use derive_more::Display; -use super::{error::Message, respond, Response}; +use super::{error::Message, Response}; const WF_CONTENT_TYPE: (&str, &str) = ("content-type", "application/jrd+json"); @@ -23,8 +23,10 @@ pub fn resolve(cx: &Context, params: &[(&str, &str)]) -> Result fuck!(400: "missing/invalid resource param"), }