publish Create/Announce on redis

This commit is contained in:
Astro 2023-10-12 22:09:09 +02:00
parent 41f75bf848
commit 38f5b2cb13
7 changed files with 110 additions and 6 deletions

62
Cargo.lock generated
View file

@ -58,6 +58,12 @@ version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "arc-swap"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6"
[[package]]
name = "askama"
version = "0.11.1"
@ -287,6 +293,7 @@ dependencies = [
"metrics",
"metrics-exporter-prometheus",
"metrics-util",
"redis",
"reqwest",
"serde",
"serde_json",
@ -342,6 +349,20 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "combine"
version = "4.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4"
dependencies = [
"bytes",
"futures-core",
"memchr",
"pin-project-lite",
"tokio",
"tokio-util",
]
[[package]]
name = "core-foundation"
version = "0.9.3"
@ -1433,6 +1454,30 @@ dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redis"
version = "0.23.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f49cdc0bb3f412bf8e7d1bd90fe1d9eb10bc5c399ba90973c14662a27b3f8ba"
dependencies = [
"arc-swap",
"async-trait",
"bytes",
"combine",
"futures",
"futures-util",
"itoa",
"percent-encoding",
"pin-project-lite",
"ryu",
"sha1_smol",
"socket2 0.4.9",
"tokio",
"tokio-retry",
"tokio-util",
"url",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
@ -1661,6 +1706,12 @@ dependencies = [
"unsafe-libyaml",
]
[[package]]
name = "sha1_smol"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
[[package]]
name = "sha2"
version = "0.10.8"
@ -1955,6 +2006,17 @@ dependencies = [
"whoami",
]
[[package]]
name = "tokio-retry"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f"
dependencies = [
"pin-project",
"rand",
"tokio",
]
[[package]]
name = "tokio-util"
version = "0.7.9"

View file

@ -18,7 +18,7 @@ serde_json = "1"
serde_yaml = "0.9"
reqwest = { version = "0.11", features = ["json", "stream"] }
sigh = "1.0"
http_digest_headers = { version="0.1.0", default-features = false, features = ["use_openssl"] }
http_digest_headers = { version = "0.1.0", default-features = false, features = ["use_openssl"] }
thiserror = "1"
http = "0.2"
chrono = "0.4"
@ -32,3 +32,4 @@ metrics-exporter-prometheus = "0.12"
deunicode = "1.3"
urlencoding = "2"
httpdate = "1"
redis = { version = "0.23", features = ["tokio-comp", "connection-manager"] }

View file

@ -18,3 +18,7 @@ priv_key_file: private-key.pem
pub_key_file: public-key.pem
# PostgreSQL
db: "host=localhost user=relay password=xyz dbname=buzzrelay"
# Optional Redis
redis:
connection: "redis://127.0.0.1:6378/"
in_topic: "relay-in"

View file

@ -1,12 +1,19 @@
use serde::Deserialize;
use sigh::{PrivateKey, PublicKey, Key};
#[derive(Clone, Deserialize)]
pub struct RedisConfig {
pub connection: String,
pub in_topic: String,
}
#[derive(Clone, Deserialize)]
pub struct Config {
pub streams: Vec<String>,
pub db: String,
pub hostname: String,
pub listen_port: u16,
pub redis: Option<RedisConfig>,
priv_key_file: String,
pub_key_file: String,
}

View file

@ -25,7 +25,7 @@ const SIGNATURE_HEADERS_REQUIRED: &[&str] = &[
pub struct Endpoint<'a> {
pub payload: serde_json::Value,
signature: Signature<'a>,
remote_actor_uri: String,
pub remote_actor_uri: String,
}
#[async_trait]

View file

@ -152,7 +152,7 @@ async fn post_relay(
).into_response();
}
};
let object_type = action.object
let object_type = action.object.as_ref()
.and_then(|object| object.get("type").cloned())
.and_then(|object_type| object_type.as_str().map(std::string::ToString::to_string));
@ -228,7 +228,27 @@ async fn post_relay(
).into_response()
}
}
} else if action.action_type == "Create" || action.action_type == "Announce" {
tracing::trace!("Received on {} from {}: {} {:?}", target.uri(), endpoint.remote_actor_uri, action.action_type, action.object);
if let Some((redis, in_topic)) = &state.redis {
if let Some(data) = &action.object
.and_then(|o| serde_json::to_vec(&o).ok())
{
if let Err(e) = redis::Cmd::publish(in_topic.as_ref(), data)
.query_async::<_, redis::Value>(&mut redis.clone())
.await
{
tracing::error!("redis publish: {}", e);
}
}
}
(StatusCode::ACCEPTED,
[("content-type", "application/activity+json")],
"{}"
).into_response()
} else {
tracing::error!("Unrecognized action type {:?}", action.action_type);
track_request("POST", "relay", "unrecognized");
(StatusCode::BAD_REQUEST, "Not a recognized request").into_response()
}
@ -312,7 +332,15 @@ async fn main() {
.expect("Call with config.yaml")
);
let database = db::Database::connect(&config.db).await;
let mut redis = None;
if let Some(redis_config) = config.redis.clone() {
let client = redis::Client::open(redis_config.connection)
.expect("redis::Client");
let manager = redis::aio::ConnectionManager::new(client)
.await
.expect("redis::Client");
redis = Some((manager, redis_config.in_topic));
}
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(5))
.user_agent(concat!(
@ -324,7 +352,7 @@ async fn main() {
.pool_idle_timeout(Some(Duration::from_secs(5)))
.build()
.unwrap();
let state = State::new(config.clone(), database, client);
let state = State::new(config.clone(), database, redis, client);
let stream_rx = stream::spawn(config.streams.clone().into_iter());
relay::spawn(state.clone(), stream_rx);

View file

@ -8,6 +8,7 @@ use crate::{config::Config, db::Database};
#[derive(Clone)]
pub struct State {
pub database: Database,
pub redis: Option<(redis::aio::ConnectionManager, Arc<String>)>,
pub client: Arc<reqwest::Client>,
pub hostname: Arc<String>,
pub priv_key: Arc<PrivateKey>,
@ -22,11 +23,12 @@ impl FromRef<State> for Arc<reqwest::Client> {
}
impl State {
pub fn new(config: Config, database: Database, client: reqwest::Client) -> Self {
pub fn new(config: Config, database: Database, redis: Option<(redis::aio::ConnectionManager, String)>, client: reqwest::Client) -> Self {
let priv_key = Arc::new(config.priv_key());
let pub_key = Arc::new(config.pub_key());
State {
database,
redis: redis.map(|(connection, in_topic)| (connection, Arc::new(in_topic))),
client: Arc::new(client),
hostname: Arc::new(config.hostname),
priv_key,