mirror of
https://github.com/astro/buzzrelay.git
synced 2024-11-22 04:00:59 +00:00
relay can be followed
This commit is contained in:
parent
6260f4306e
commit
177051be52
5 changed files with 58 additions and 13 deletions
|
@ -26,8 +26,11 @@ pub struct ActorPublicKey {
|
||||||
/// ActivityPub "activity"
|
/// ActivityPub "activity"
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Action<O> {
|
pub struct Action<O> {
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
pub jsonld_context: serde_json::Value,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub action_type: String,
|
pub action_type: String,
|
||||||
|
pub id: String,
|
||||||
pub actor: String,
|
pub actor: String,
|
||||||
pub to: Option<String>,
|
pub to: Option<String>,
|
||||||
pub object: Option<O>,
|
pub object: Option<O>,
|
||||||
|
|
|
@ -85,7 +85,6 @@ where
|
||||||
// mastodon uses base64::alphabet::STANDARD, not base64::alphabet::URL_SAFE
|
// mastodon uses base64::alphabet::STANDARD, not base64::alphabet::URL_SAFE
|
||||||
digest_header = digest_header.replace("+", "-")
|
digest_header = digest_header.replace("+", "-")
|
||||||
.replace("/", "_");
|
.replace("/", "_");
|
||||||
dbg!(&digest_header);
|
|
||||||
let digest: DigestHeader = digest_header.parse()
|
let digest: DigestHeader = digest_header.parse()
|
||||||
.map_err(|e| (StatusCode::BAD_REQUEST, format!("Cannot parse Digest: header: {}", e)))?;
|
.map_err(|e| (StatusCode::BAD_REQUEST, format!("Cannot parse Digest: header: {}", e)))?;
|
||||||
// read body
|
// read body
|
||||||
|
|
13
src/main.rs
13
src/main.rs
|
@ -17,6 +17,7 @@ pub use fetch::fetch;
|
||||||
mod send;
|
mod send;
|
||||||
pub use send::send;
|
pub use send::send;
|
||||||
mod activitypub;
|
mod activitypub;
|
||||||
|
mod webfinger;
|
||||||
mod endpoint;
|
mod endpoint;
|
||||||
|
|
||||||
const ACTOR_ID: &str = "https://relay.fedi.buzz/actor";
|
const ACTOR_ID: &str = "https://relay.fedi.buzz/actor";
|
||||||
|
@ -61,6 +62,7 @@ async fn handler(
|
||||||
axum::extract::State(state): axum::extract::State<State>,
|
axum::extract::State(state): axum::extract::State<State>,
|
||||||
endpoint: endpoint::Endpoint,
|
endpoint: endpoint::Endpoint,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
|
dbg!(&endpoint);
|
||||||
let action = match serde_json::from_value::<activitypub::Action<serde_json::Value>>(endpoint.payload.clone()) {
|
let action = match serde_json::from_value::<activitypub::Action<serde_json::Value>>(endpoint.payload.clone()) {
|
||||||
Ok(action) => action,
|
Ok(action) => action,
|
||||||
Err(e) => return (
|
Err(e) => return (
|
||||||
|
@ -68,29 +70,29 @@ async fn handler(
|
||||||
format!("Bad action: {:?}", e)
|
format!("Bad action: {:?}", e)
|
||||||
).into_response(),
|
).into_response(),
|
||||||
};
|
};
|
||||||
dbg!(&action);
|
|
||||||
|
|
||||||
if action.action_type == "Follow" {
|
if action.action_type == "Follow" {
|
||||||
let private_key = state.private_key.clone();
|
let private_key = state.private_key.clone();
|
||||||
let client = state.client.clone();
|
let client = state.client.clone();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let accept = activitypub::Action {
|
let accept = activitypub::Action {
|
||||||
|
jsonld_context: serde_json::Value::String("https://www.w3.org/ns/activitystreams".to_string()),
|
||||||
action_type: "Accept".to_string(),
|
action_type: "Accept".to_string(),
|
||||||
actor: ACTOR_ID.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),
|
object: Some(endpoint.payload),
|
||||||
};
|
};
|
||||||
dbg!(serde_json::to_string_pretty(&accept));
|
|
||||||
send::send(
|
send::send(
|
||||||
client.as_ref(), &endpoint.actor.inbox,
|
client.as_ref(), &endpoint.actor.inbox,
|
||||||
ACTOR_KEY,
|
ACTOR_KEY,
|
||||||
&private_key,
|
&private_key,
|
||||||
accept,
|
accept,
|
||||||
).await
|
).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")],
|
[("content-type", "application/activity+json")],
|
||||||
"{}"
|
"{}"
|
||||||
).into_response()
|
).into_response()
|
||||||
|
@ -115,6 +117,7 @@ async fn main() {
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/actor", get(actor))
|
.route("/actor", get(actor))
|
||||||
.route("/relay", post(handler))
|
.route("/relay", post(handler))
|
||||||
|
.route("/.well-known/webfinger", get(webfinger::webfinger))
|
||||||
.with_state(State {
|
.with_state(State {
|
||||||
client: Arc::new(reqwest::Client::new()),
|
client: Arc::new(reqwest::Client::new()),
|
||||||
private_key, public_key,
|
private_key, public_key,
|
||||||
|
|
23
src/send.rs
23
src/send.rs
|
@ -1,3 +1,4 @@
|
||||||
|
use http::StatusCode;
|
||||||
use http_digest_headers::{DigestHeader, DigestMethod};
|
use http_digest_headers::{DigestHeader, DigestMethod};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sigh::{PrivateKey, SigningConfig, alg::RsaSha256};
|
use sigh::{PrivateKey, SigningConfig, alg::RsaSha256};
|
||||||
|
@ -14,11 +15,15 @@ pub enum SendError {
|
||||||
HttpReq(#[from] http::Error),
|
HttpReq(#[from] http::Error),
|
||||||
#[error("HTTP client error")]
|
#[error("HTTP client error")]
|
||||||
Http(#[from] reqwest::Error),
|
Http(#[from] reqwest::Error),
|
||||||
|
#[error("Invalid URI")]
|
||||||
|
InvalidUri,
|
||||||
|
#[error("Error response from remote")]
|
||||||
|
Response(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn send<T: Serialize>(
|
pub async fn send<T: Serialize>(
|
||||||
client: &reqwest::Client,
|
client: &reqwest::Client,
|
||||||
url: &str,
|
uri: &str,
|
||||||
key_id: &str,
|
key_id: &str,
|
||||||
private_key: &PrivateKey,
|
private_key: &PrivateKey,
|
||||||
body: T,
|
body: T,
|
||||||
|
@ -38,9 +43,12 @@ pub async fn send<T: Serialize>(
|
||||||
&digest_header[7..].replace("-", "+").replace("_", "/")
|
&digest_header[7..].replace("-", "+").replace("_", "/")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let url = reqwest::Url::parse(uri)
|
||||||
|
.map_err(|_| SendError::InvalidUri)?;
|
||||||
let mut req = http::Request::builder()
|
let mut req = http::Request::builder()
|
||||||
.method("POST")
|
.method("POST")
|
||||||
.uri(url)
|
.uri(uri)
|
||||||
|
.header("host", format!("{}", url.host().ok_or(SendError::InvalidUri)?))
|
||||||
.header("content-type", "application/activity+json")
|
.header("content-type", "application/activity+json")
|
||||||
.header("date", chrono::Utc::now().to_rfc2822()
|
.header("date", chrono::Utc::now().to_rfc2822()
|
||||||
.replace("+0000", "GMT"))
|
.replace("+0000", "GMT"))
|
||||||
|
@ -49,11 +57,12 @@ pub async fn send<T: Serialize>(
|
||||||
.map_err(SendError::HttpReq)?;
|
.map_err(SendError::HttpReq)?;
|
||||||
SigningConfig::new(RsaSha256, private_key, key_id)
|
SigningConfig::new(RsaSha256, private_key, key_id)
|
||||||
.sign(&mut req)?;
|
.sign(&mut req)?;
|
||||||
dbg!(&req);
|
|
||||||
let res = client.execute(req.try_into()?)
|
let res = client.execute(req.try_into()?)
|
||||||
.await?;
|
.await?;
|
||||||
dbg!(&res);
|
if res.status() >= StatusCode::OK && res.status() < StatusCode::MULTIPLE_CHOICES {
|
||||||
dbg!(res.text().await);
|
Ok(())
|
||||||
|
} else {
|
||||||
Ok(())
|
let response = res.text().await?;
|
||||||
|
Err(SendError::Response(response))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
31
src/webfinger.rs
Normal file
31
src/webfinger.rs
Normal 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()
|
||||||
|
}
|
Loading…
Reference in a new issue