relay can be followed

This commit is contained in:
Astro 2022-12-13 04:12:35 +01:00
parent 6260f4306e
commit 177051be52
5 changed files with 58 additions and 13 deletions

View file

@ -26,8 +26,11 @@ pub struct ActorPublicKey {
/// ActivityPub "activity"
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Action<O> {
#[serde(rename = "@context")]
pub jsonld_context: serde_json::Value,
#[serde(rename = "type")]
pub action_type: String,
pub id: String,
pub actor: String,
pub to: Option<String>,
pub object: Option<O>,

View file

@ -85,7 +85,6 @@ where
// mastodon uses base64::alphabet::STANDARD, not base64::alphabet::URL_SAFE
digest_header = digest_header.replace("+", "-")
.replace("/", "_");
dbg!(&digest_header);
let digest: DigestHeader = digest_header.parse()
.map_err(|e| (StatusCode::BAD_REQUEST, format!("Cannot parse Digest: header: {}", e)))?;
// read body

View file

@ -17,6 +17,7 @@ pub use fetch::fetch;
mod send;
pub use send::send;
mod activitypub;
mod webfinger;
mod endpoint;
const ACTOR_ID: &str = "https://relay.fedi.buzz/actor";
@ -61,6 +62,7 @@ async fn handler(
axum::extract::State(state): axum::extract::State<State>,
endpoint: endpoint::Endpoint,
) -> Response {
dbg!(&endpoint);
let action = match serde_json::from_value::<activitypub::Action<serde_json::Value>>(endpoint.payload.clone()) {
Ok(action) => action,
Err(e) => return (
@ -68,29 +70,29 @@ async fn handler(
format!("Bad action: {:?}", e)
).into_response(),
};
dbg!(&action);
if action.action_type == "Follow" {
let private_key = state.private_key.clone();
let client = state.client.clone();
tokio::spawn(async move {
let accept = activitypub::Action {
jsonld_context: serde_json::Value::String("https://www.w3.org/ns/activitystreams".to_string()),
action_type: "Accept".to_string(),
actor: ACTOR_ID.to_string(),
to: Some(endpoint.actor.id),
to: Some(endpoint.actor.id.clone()),
id: action.id,
object: Some(endpoint.payload),
};
dbg!(serde_json::to_string_pretty(&accept));
send::send(
client.as_ref(), &endpoint.actor.inbox,
ACTOR_KEY,
&private_key,
accept,
).await
.map_err(|e| tracing::error!("post: {}", e));
.map_err(|e| tracing::error!("post accept: {}", e));
});
(StatusCode::CREATED,
(StatusCode::ACCEPTED,
[("content-type", "application/activity+json")],
"{}"
).into_response()
@ -115,6 +117,7 @@ async fn main() {
let app = Router::new()
.route("/actor", get(actor))
.route("/relay", post(handler))
.route("/.well-known/webfinger", get(webfinger::webfinger))
.with_state(State {
client: Arc::new(reqwest::Client::new()),
private_key, public_key,

View file

@ -1,3 +1,4 @@
use http::StatusCode;
use http_digest_headers::{DigestHeader, DigestMethod};
use serde::Serialize;
use sigh::{PrivateKey, SigningConfig, alg::RsaSha256};
@ -14,11 +15,15 @@ pub enum SendError {
HttpReq(#[from] http::Error),
#[error("HTTP client error")]
Http(#[from] reqwest::Error),
#[error("Invalid URI")]
InvalidUri,
#[error("Error response from remote")]
Response(String),
}
pub async fn send<T: Serialize>(
client: &reqwest::Client,
url: &str,
uri: &str,
key_id: &str,
private_key: &PrivateKey,
body: T,
@ -38,9 +43,12 @@ pub async fn send<T: Serialize>(
&digest_header[7..].replace("-", "+").replace("_", "/")
);
let url = reqwest::Url::parse(uri)
.map_err(|_| SendError::InvalidUri)?;
let mut req = http::Request::builder()
.method("POST")
.uri(url)
.uri(uri)
.header("host", format!("{}", url.host().ok_or(SendError::InvalidUri)?))
.header("content-type", "application/activity+json")
.header("date", chrono::Utc::now().to_rfc2822()
.replace("+0000", "GMT"))
@ -49,11 +57,12 @@ pub async fn send<T: Serialize>(
.map_err(SendError::HttpReq)?;
SigningConfig::new(RsaSha256, private_key, key_id)
.sign(&mut req)?;
dbg!(&req);
let res = client.execute(req.try_into()?)
.await?;
dbg!(&res);
dbg!(res.text().await);
Ok(())
if res.status() >= StatusCode::OK && res.status() < StatusCode::MULTIPLE_CHOICES {
Ok(())
} else {
let response = res.text().await?;
Err(SendError::Response(response))
}
}

31
src/webfinger.rs Normal file
View file

@ -0,0 +1,31 @@
use std::collections::HashMap;
use axum::{
async_trait,
body::{Bytes, HttpBody},
extract::{Query},
http::{header::CONTENT_TYPE, Request, StatusCode},
Json,
response::{IntoResponse, Response},
routing::post,
Form, RequestExt, Router, BoxError,
};
use serde_json::json;
pub async fn webfinger(Query(params): Query<HashMap<String, String>>) -> Response {
let resource = match params.get("resource") {
Some(resource) => resource,
None => return StatusCode::NOT_FOUND.into_response(),
};
Json(json!({
"subject": &resource,
"aliases": &[
"https://relay.fedi.buzz/actor",
],
"links": &[json!({
"rel": "self",
"type": "application/activity+json",
"href": "https://relay.fedi.buzz/actor",
})],
})).into_response()
}