diff --git a/Cargo.lock b/Cargo.lock index 4335a93..2b73e6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -1695,6 +1695,17 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kdl" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062c875482ccb676fd40c804a40e3824d4464c18c364547456d1c8e8e951ae47" +dependencies = [ + "miette 5.10.0", + "nom", + "thiserror", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1783,6 +1794,7 @@ dependencies = [ "headers", "hyper", "itertools", + "kdl", "lru", "magnetar_common", "magnetar_core", @@ -1793,7 +1805,7 @@ dependencies = [ "magnetar_runtime", "magnetar_sdk", "magnetar_webfinger", - "miette", + "miette 7.2.0", "percent-encoding", "quick-xml", "rmp-serde", @@ -1839,7 +1851,7 @@ dependencies = [ "headers", "hyper", "magnetar_common", - "miette", + "miette 7.2.0", "percent-encoding", "serde", "serde_json", @@ -1897,7 +1909,7 @@ dependencies = [ "magnetar_core", "magnetar_host_meta", "magnetar_webfinger", - "miette", + "miette 7.2.0", "percent-encoding", "quick-xml", "reqwest", @@ -1982,7 +1994,7 @@ dependencies = [ "itertools", "magnetar_core", "magnetar_sdk", - "miette", + "miette 7.2.0", "thiserror", "tracing", ] @@ -2051,6 +2063,18 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "miette" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" +dependencies = [ + "miette-derive 5.10.0", + "once_cell", + "thiserror", + "unicode-width", +] + [[package]] name = "miette" version = "7.2.0" @@ -2060,7 +2084,7 @@ dependencies = [ "backtrace", "backtrace-ext", "cfg-if", - "miette-derive", + "miette-derive 7.2.0", "owo-colors", "supports-color", "supports-hyperlinks", @@ -2071,6 +2095,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "miette-derive" +version = "5.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "miette-derive" version = "7.2.0" diff --git a/Cargo.toml b/Cargo.toml index 09c1788..4561bd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ hyper = "1.1" idna = "1" indexmap = "2.2" itertools = "0.13" +kdl = "4" lru = "0.12" miette = "7" nom = "7" @@ -133,6 +134,7 @@ thiserror = { workspace = true } percent-encoding = { workspace = true } +kdl = { workspace = true } rmp-serde = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } diff --git a/config/.gitignore b/config/.gitignore index 73ee336..e5ef343 100644 --- a/config/.gitignore +++ b/config/.gitignore @@ -1,3 +1,4 @@ * !.gitignore -!default.toml \ No newline at end of file +!default.toml +!default-vars.kdl diff --git a/config/default-vars.kdl b/config/default-vars.kdl new file mode 100644 index 0000000..96dff1b --- /dev/null +++ b/config/default-vars.kdl @@ -0,0 +1,34 @@ +cache { + local-user-cache { + // Size is unlimited + lifetime 300min + } + + emoji-cache { + size 4096 + } + + remote-instance-cache { + size 256 + lifetime 100s + } + + drive-file-cache { + size 128 + lifetime 10s + } +} + +api-model { + note { + buffer 10 + } + + notification { + buffer 10 + } +} + +activity-pub { + user-agent "magnetar/$version ($host)" +} diff --git a/ext_federation/src/ap_client.rs b/ext_federation/src/ap_client.rs index 4731306..acd1f9b 100644 --- a/ext_federation/src/ap_client.rs +++ b/ext_federation/src/ap_client.rs @@ -13,12 +13,19 @@ use magnetar_core::web_model::content_type::ContentActivityStreams; use crate::{ client::federation_client::{FederationClient, FederationClientError}, crypto::{ApSigningError, ApSigningKey, SigningAlgorithm}, - ApClientService, ApSignature, ApSigningField, - ApSigningHeaders, SigningInput, SigningParts, + ApClientService, ApSignature, ApSigningField, ApSigningHeaders, SigningInput, SigningParts, }; pub struct ApClientServiceDefaultProvider { - client: Arc, + client: Arc + Send + Sync>, +} + +impl ApClientServiceDefaultProvider { + pub fn new(client: impl AsRef + Send + Sync + 'static) -> Self { + Self { + client: Arc::new(client), + } + } } impl Display for ApSignature { @@ -238,7 +245,7 @@ impl ApClientService for ApClientServiceDefaultProvider { &self, signing_key: ApSigningKey<'_>, signing_algorithm: SigningAlgorithm, - request: impl SigningInput, + request: &dyn SigningInput, ) -> Result { let components = request.create_signing_input(); @@ -278,7 +285,7 @@ impl ApClientService for ApClientServiceDefaultProvider { SigningAlgorithm::RsaSha256 => self.sign_request( signing_key, signing_algorithm, - SigningInputGetRsaSha256 { + &SigningInputGetRsaSha256 { request_target: RequestTarget { url: &url, method: Method::GET, @@ -291,7 +298,7 @@ impl ApClientService for ApClientServiceDefaultProvider { SigningAlgorithm::Hs2019 => self.sign_request( signing_key, signing_algorithm, - SigningInputGetHs2019 { + &SigningInputGetHs2019 { request_target: RequestTarget { url: &url, method: Method::GET, @@ -319,6 +326,8 @@ impl ApClientService for ApClientServiceDefaultProvider { Ok(self .client + .as_ref() + .as_ref() .get(url) .accept(ContentActivityStreams) .headers(headers) @@ -346,7 +355,7 @@ impl ApClientService for ApClientServiceDefaultProvider { SigningAlgorithm::RsaSha256 => self.sign_request( signing_key, signing_algorithm, - SigningInputPostRsaSha256 { + &SigningInputPostRsaSha256 { request_target: RequestTarget { url: &url, method: Method::POST, @@ -360,7 +369,7 @@ impl ApClientService for ApClientServiceDefaultProvider { SigningAlgorithm::Hs2019 => self.sign_request( signing_key, signing_algorithm, - SigningInputPostHs2019 { + &SigningInputPostHs2019 { request_target: RequestTarget { url: &url, method: Method::POST, @@ -395,6 +404,8 @@ impl ApClientService for ApClientServiceDefaultProvider { Ok(self .client + .as_ref() + .as_ref() .builder(Method::POST, url) .accept(ContentActivityStreams) .content_type(ContentActivityStreams) @@ -428,15 +439,15 @@ mod test { let rsa_key = rsa::RsaPrivateKey::from_pkcs8_pem(key.trim()).into_diagnostic()?; let ap_client = ApClientServiceDefaultProvider { - client: Arc::new( + client: Arc::new(Box::new( FederationClient::new( true, 128_000, 25, UserAgent::from_static("magnetar/0.42 (https://astolfo.social)"), ) - .into_diagnostic()?, - ), + .into_diagnostic()?, + )), }; let val = ap_client diff --git a/ext_federation/src/lib.rs b/ext_federation/src/lib.rs index a7b9154..473e0b4 100644 --- a/ext_federation/src/lib.rs +++ b/ext_federation/src/lib.rs @@ -154,7 +154,7 @@ pub trait ApClientService: Send + Sync { &self, signing_key: ApSigningKey<'_>, signing_algorithm: SigningAlgorithm, - request: impl SigningInput, + request: &dyn SigningInput, ) -> Result; async fn signed_get( diff --git a/src/main.rs b/src/main.rs index b006b9f..ac86e90 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ pub mod nodeinfo; mod rpc_v1; pub mod service; pub mod util; +pub mod vars; pub mod web; pub mod webfinger; @@ -19,7 +20,8 @@ use futures::{select, FutureExt}; use magnetar_common::config::{MagnetarConfig, MagnetarRpcSocketKind}; use magnetar_model::{CacheConnectorConfig, CalckeyCache, CalckeyModel, ConnectorConfig}; use miette::{miette, IntoDiagnostic}; -use rpc_v1::proto::{MagRpc, RpcMessage, RpcSockAddr}; +use rpc_v1::create_rpc_router; +use rpc_v1::proto::RpcSockAddr; use std::convert::Infallible; use std::future::Future; use std::net::SocketAddr; @@ -28,8 +30,8 @@ use tokio::net::TcpListener; use tokio::signal; use tower_http::cors::{Any, CorsLayer}; use tower_http::trace::TraceLayer; +use tracing::info; use tracing::log::error; -use tracing::{debug, info}; use tracing_subscriber::EnvFilter; #[tokio::main] @@ -53,21 +55,17 @@ async fn main() -> miette::Result<()> { let db = CalckeyModel::new(ConnectorConfig { url: config.data.database_url.clone(), }) - .await - .into_diagnostic()?; + .await + .into_diagnostic()?; db.migrate().await.into_diagnostic()?; let redis = CalckeyCache::new(CacheConnectorConfig { url: config.data.redis_url.clone(), }) - .into_diagnostic()?; + .into_diagnostic()?; - let service = Arc::new( - MagnetarService::new(config, db.clone(), redis) - .await - .into_diagnostic()?, - ); + let service = Arc::new(MagnetarService::new(config, db.clone(), redis).await?); let shutdown_signal = shutdown_signal().shared(); @@ -80,7 +78,7 @@ async fn main() -> miette::Result<()> { async fn run_rpc( service: Arc, config: &'static MagnetarConfig, - shutdown_signal: impl Future + Send + 'static, + shutdown_signal: impl Future + Send + 'static, ) -> miette::Result<()> { let rpc_bind_addr = match &config.rpc.connection_settings { MagnetarRpcSocketKind::None => { @@ -91,13 +89,7 @@ async fn run_rpc( MagnetarRpcSocketKind::Tcp(ip) => RpcSockAddr::Ip(*ip), }; - let rpc = MagRpc::new().handle( - "/ping", - |_, RpcMessage(message): RpcMessage| async move { - debug!("Received RPC ping: {}", message); - RpcMessage("pong".to_owned()) - }, - ); + let rpc = create_rpc_router(); rpc.run(service, rpc_bind_addr, Some(shutdown_signal)).await } @@ -105,7 +97,7 @@ async fn run_rpc( async fn run_web( service: Arc, config: &'static MagnetarConfig, - shutdown_signal: impl Future + Send + 'static, + shutdown_signal: impl Future + Send + 'static, ) -> miette::Result<()> { let well_known_router = Router::new() .route( diff --git a/src/rpc_v1/mod.rs b/src/rpc_v1/mod.rs index febacec..3def20a 100644 --- a/src/rpc_v1/mod.rs +++ b/src/rpc_v1/mod.rs @@ -1 +1,98 @@ +use std::sync::Arc; + +use magnetar_federation::crypto::SigningAlgorithm; +use proto::{MagRpc, RpcMessage}; +use serde::Deserialize; +use tracing::{debug, info}; + +use crate::{ + model::{processing::note::NoteModel, PackingContext}, + service::MagnetarService, + web::{ApiError, ObjectNotFound}, +}; + pub mod proto; + +#[derive(Debug, Deserialize)] +struct RpcApGet { + user_id: String, + url: String, +} + +#[derive(Debug, Deserialize)] +struct RpcApPost { + user_id: String, + url: String, + body: serde_json::Value, +} + +pub fn create_rpc_router() -> MagRpc { + MagRpc::new() + .handle( + "/ping", + |_, RpcMessage(message): RpcMessage| async move { + debug!("Received RPC ping: {}", message); + RpcMessage("pong".to_owned()) + }, + ) + .handle( + "/note/by-id", + |service, RpcMessage(id): RpcMessage| async move { + let ctx = PackingContext::new(service, None).await?; + let note = NoteModel { + attachments: true, + with_context: true, + } + .fetch_single(&ctx, &id) + .await? + .ok_or(ObjectNotFound(id))?; + + Result::<_, ApiError>::Ok(note) + }, + ) + .handle( + "/ap/get", + |service: Arc, + RpcMessage(RpcApGet { user_id, url }): RpcMessage| async move { + let Some(user) = service.local_user_cache.get_by_id(&user_id).await? else { + return Err(ObjectNotFound(format!("LocalUserID:{user_id}")).into()); + }; + + let key_id = format!( + "https://{}/users/{}#main-key", + service.config.networking.host, user_id + ); + let signing_key = user + .private_key + .create_signing_key(&key_id, SigningAlgorithm::RsaSha256)?; + let result = service + .ap_client + .signed_get(signing_key, SigningAlgorithm::RsaSha256, None, &url) + .await?; + + Result::<_, ApiError>::Ok(result) + }, + ) + .handle( + "/ap/post", + |service: Arc, + RpcMessage(RpcApPost { user_id, url, body }): RpcMessage| async move { + let Some(user) = service.local_user_cache.get_by_id(&user_id).await? else { + return Err(ObjectNotFound(format!("LocalUserID:{user_id}")).into()); + }; + + let key_id = format!( + "https://{}/users/{}#main-key", + service.config.networking.host, user_id + ); + let signing_key = user + .private_key + .create_signing_key(&key_id, SigningAlgorithm::RsaSha256)?; + let result = service + .ap_client + .signed_post(signing_key, SigningAlgorithm::RsaSha256, None, &url, &body) + .await?; + Result::<_, ApiError>::Ok(result) + }, + ) +} diff --git a/src/rpc_v1/proto.rs b/src/rpc_v1/proto.rs index 44270cc..2998b85 100644 --- a/src/rpc_v1/proto.rs +++ b/src/rpc_v1/proto.rs @@ -1,7 +1,6 @@ use crate::service::MagnetarService; use bytes::BufMut; -use futures::{sink, FutureExt, Stream, StreamExt}; -use futures_util::SinkExt; +use futures::{FutureExt, Stream, StreamExt}; use miette::{miette, IntoDiagnostic}; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; @@ -37,7 +36,7 @@ pub struct RpcResponse(Vec); impl IntoRpcResponse for RpcMessage { fn into_rpc_response(self) -> Option { - rmp_serde::to_vec(&self) + rmp_serde::to_vec_named(&self) .inspect_err(|e| { error!( "Failed to serialize value of type {}: {}", @@ -223,14 +222,16 @@ impl MagRpc { r.ok() }) - .filter_map(|(payload, listener)| { + .filter_map(|(serial, payload, listener)| { let ctx = context.clone(); - async move { listener(ctx, payload).await } + async move { Some((serial, listener(ctx, payload).await?)) } }); futures::pin_mut!(src); - while let Some(RpcResponse(bytes)) = src.next().await { + while let Some((serial, RpcResponse(bytes))) = src.next().await { + write_half.write_u8(b'M').await.into_diagnostic()?; + write_half.write_u64(serial).await.into_diagnostic()?; write_half .write_u32(bytes.len() as u32) .await @@ -315,14 +316,16 @@ impl MagRpc { r.ok() }) - .filter_map(|(payload, listener)| { + .filter_map(|(serial, payload, listener)| { let ctx = context.clone(); - async move { listener(ctx, payload).await } + async move { Some((serial, listener(ctx, payload).await?)) } }); futures::pin_mut!(src); - while let Some(RpcResponse(bytes)) = src.next().await { + while let Some((serial, RpcResponse(bytes))) = src.next().await { + write_half.write_u8(b'M').await.into_diagnostic()?; + write_half.write_u64(serial).await.into_diagnostic()?; write_half .write_u32(bytes.len() as u32) .await @@ -364,7 +367,7 @@ impl RpcCallDecoder { &self, mut buf_read: BufReader, mut cancel: tokio::sync::oneshot::Receiver<()>, - ) -> impl Stream)>> + Send + 'static + ) -> impl Stream)>> + Send + 'static { let decoders = self.payload_decoders.clone(); let listeners = self.listeners.clone(); @@ -395,6 +398,8 @@ impl RpcCallDecoder { )).into_diagnostic(); } + let serial = buf_read.read_u64().await.into_diagnostic()?; + let name_len = buf_read.read_u32().await.into_diagnostic()? as usize; if name_len > name_buf.capacity() { name_buf.reserve(name_len - name_buf.capacity()); @@ -428,10 +433,10 @@ impl RpcCallDecoder { buf_read.read_buf(&mut buf_write).await.into_diagnostic()?; } - miette::Result::<_>::Ok(Some((name_buf_write, buf_write))) + miette::Result::<_>::Ok(Some((serial, name_buf_write, buf_write))) }; - let Some((name_buf_write, payload)) = select! { + let Some((serial, name_buf_write, payload)) = select! { read_result = read_fut => read_result, _ = &mut cancel => { break; } }? else { @@ -457,7 +462,7 @@ impl RpcCallDecoder { } }; - yield (packet, listener); + yield (serial, packet, listener); messages += 1; } } diff --git a/src/service/federation_client.rs b/src/service/federation_client.rs new file mode 100644 index 0000000..ab8e526 --- /dev/null +++ b/src/service/federation_client.rs @@ -0,0 +1,32 @@ +use std::str::FromStr; + +use headers::UserAgent; +use magnetar_common::config::MagnetarConfig; +use magnetar_federation::{ + ap_client::{ApClientError, ApClientServiceDefaultProvider}, + client::federation_client::FederationClient, + ApClientService, +}; +use miette::IntoDiagnostic; + +pub(super) fn new_federation_client_service( + config: &'static MagnetarConfig, +) -> miette::Result { + FederationClient::new( + true, + 256000, + 20, + UserAgent::from_str(&format!( + "magnetar/{} (https://{})", + config.branding.version, config.networking.host + )) + .into_diagnostic()?, + ) + .into_diagnostic() +} + +pub(super) fn new_ap_client_service( + federation_client: impl AsRef + Send + Sync + 'static, +) -> impl ApClientService { + ApClientServiceDefaultProvider::new(federation_client) +} diff --git a/src/service/mod.rs b/src/service/mod.rs index 11036c9..be46cde 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -1,17 +1,23 @@ +use federation_client::{new_ap_client_service, new_federation_client_service}; use gen_id::GenIdService; use magnetar_common::config::MagnetarConfig; +use magnetar_federation::ap_client::ApClientError; +use magnetar_federation::client::federation_client::FederationClient; +use magnetar_federation::ApClientService; use magnetar_model::{ck, CalckeyCache, CalckeyModel}; use std::fmt::{Debug, Formatter}; use std::time::Duration; -use thiserror::Error; pub mod emoji_cache; +pub mod federation_client; pub mod gen_id; pub mod generic_id_cache; pub mod instance_cache; pub mod instance_meta_cache; pub mod local_user_cache; +pub type ApClient = dyn ApClientService + Send + Sync; + #[non_exhaustive] pub struct MagnetarService { pub db: CalckeyModel, @@ -23,6 +29,8 @@ pub struct MagnetarService { pub remote_instance_cache: instance_cache::RemoteInstanceCacheService, pub emoji_cache: emoji_cache::EmojiCacheService, pub drive_file_cache: generic_id_cache::GenericIdCacheService, + pub federation_client: FederationClient, + pub ap_client: Box, } impl Debug for MagnetarService { @@ -35,18 +43,12 @@ impl Debug for MagnetarService { } } -#[derive(Debug, Error)] -pub enum ServiceInitError { - #[error("Authentication cache initialization error: {0}")] - AuthCacheError(#[from] local_user_cache::UserCacheError), -} - impl MagnetarService { pub async fn new( config: &'static MagnetarConfig, db: CalckeyModel, cache: CalckeyCache, - ) -> Result { + ) -> miette::Result { let local_user_cache = local_user_cache::LocalUserCacheService::new(config, db.clone(), cache.clone()).await?; let instance_meta_cache = instance_meta_cache::InstanceMetaCacheService::new(db.clone()); @@ -60,6 +62,9 @@ impl MagnetarService { let drive_file_cache = generic_id_cache::GenericIdCacheService::new(db.clone(), 128, Duration::from_secs(10)); + let federation_client = new_federation_client_service(config)?; + let ap_client = Box::new(new_ap_client_service(Box::new(federation_client.clone()))); + Ok(Self { db, cache, @@ -70,6 +75,8 @@ impl MagnetarService { emoji_cache, drive_file_cache, gen_id: GenIdService, + federation_client, + ap_client, }) } diff --git a/src/vars.rs b/src/vars.rs new file mode 100644 index 0000000..edf93d9 --- /dev/null +++ b/src/vars.rs @@ -0,0 +1,18 @@ +//! Dynamic configuration variables that would be very messy to configure from the environment +//! +//! Most of these values were arbitrarily chosen, so your mileage may vary when adjusting these. +//! +//! Larger instances may benefit from higher cache sizes and increased parallelism, while smaller +//! ones may want to opt for memory savings. + +use miette::IntoDiagnostic; + +#[derive(Debug)] +pub struct MagVars {} + +fn parse_vars() -> miette::Result { + let default_cfg = std::fs::read_to_string("config/default-vars.kdl").into_diagnostic()?; + let doc = default_cfg.parse::().into_diagnostic()?; + + Ok(MagVars {}) +} diff --git a/src/web/mod.rs b/src/web/mod.rs index c5d94b1..e489037 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -3,9 +3,13 @@ use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use axum::Json; use magnetar_common::util::FediverseTagParseError; -use magnetar_federation::crypto::{ApHttpPrivateKeyParseError, ApHttpPublicKeyParseError}; +use magnetar_federation::ap_client::ApClientError; +use magnetar_federation::crypto::{ + ApHttpPrivateKeyParseError, ApHttpPublicKeyParseError, ApSigningError, +}; use magnetar_model::{CalckeyCacheError, CalckeyDbError}; use miette::{diagnostic, miette, Diagnostic, Report}; +use serde::Serialize; use serde_json::json; use std::borrow::Cow; use std::fmt::Display; @@ -17,7 +21,6 @@ pub mod auth; pub mod extractors; pub mod pagination; - #[derive(Debug, Error)] #[error("API Error")] pub struct ApiError { @@ -27,10 +30,33 @@ pub struct ApiError { pub cause: miette::Report, } +#[derive(Debug, Serialize)] +pub struct ApiErrorBare<'a> { + pub status: u16, + pub nonce: &'a str, + pub message: &'a str, +} + +impl Serialize for ApiError { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + ApiErrorBare { + status: self.status.as_u16(), + nonce: &self.nonce.to_string(), + message: &self.message, + } + .serialize(serializer) + } +} + impl ApiError { - pub fn new(status: StatusCode, - message: impl Into>, - cause: impl Into) -> Self { + pub fn new( + status: StatusCode, + message: impl Into>, + cause: impl Into, + ) -> Self { Self { status, nonce: Ulid::new(), @@ -39,8 +65,7 @@ impl ApiError { } } - pub fn internal(message: impl Into>, - cause: impl Into) -> Self { + pub fn internal(message: impl Into>, cause: impl Into) -> Self { Self::new(StatusCode::INTERNAL_SERVER_ERROR, message, cause) } } @@ -50,10 +75,16 @@ impl IntoResponse for ApiError { let mut buf = [0; ulid::ULID_LEN]; let nonce = self.nonce.array_to_str(&mut buf); - warn!("[status={},nonce={}] {}", self.status.as_str(), nonce, self.cause); + warn!( + "[status={},nonce={}] {}", + self.status.as_str(), + nonce, + self.cause + ); - - let code = self.cause.code() + let code = self + .cause + .code() .as_deref() .map(::to_string); @@ -83,9 +114,11 @@ impl From for ApiError { impl From for ApiError { fn from(err: FediverseTagParseError) -> Self { - Self::new(StatusCode::BAD_REQUEST, - "Fediverse tag parse error", - miette!(code = "mag::access_forbidden", "{}", err)) + Self::new( + StatusCode::BAD_REQUEST, + "Fediverse tag parse error", + miette!(code = "mag::access_forbidden", "{}", err), + ) } } @@ -103,7 +136,10 @@ impl From for ApiError { impl From for ApiError { fn from(err: PackError) -> Self { - Self::internal("Data transformation error", miette!(code = "mag::pack_error", "{}", err)) + Self::internal( + "Data transformation error", + miette!(code = "mag::pack_error", "{}", err), + ) } } @@ -125,20 +161,46 @@ pub struct ArgumentOutOfRange(pub String); impl From for ApiError { fn from(err: ArgumentOutOfRange) -> Self { - Self::new(StatusCode::BAD_REQUEST, format!("Argument out of range: {}", err.0), err) + Self::new( + StatusCode::BAD_REQUEST, + format!("Argument out of range: {}", err.0), + err, + ) } } impl From for ApiError { fn from(err: ApHttpPublicKeyParseError) -> Self { - Self::internal("User public key parse error", - miette!(code = "mag::ap_http_public_key_parse_error", "{}", err)) + Self::internal( + "User public key parse error", + miette!(code = "mag::ap_http_public_key_parse_error", "{}", err), + ) } } impl From for ApiError { fn from(err: ApHttpPrivateKeyParseError) -> Self { - Self::internal("User private key parse error", - miette!(code = "mag::ap_http_private_key_parse_error", "{}", err)) + Self::internal( + "User private key parse error", + miette!(code = "mag::ap_http_private_key_parse_error", "{}", err), + ) + } +} + +impl From for ApiError { + fn from(err: ApSigningError) -> Self { + Self::internal( + "ActivityPub HTTP signing error", + miette!(code = "mag::ap_signing_error", "{}", err), + ) + } +} + +impl From for ApiError { + fn from(err: ApClientError) -> Self { + Self::internal( + "ActivityPub client error", + miette!(code = "mag::ap_client_error", "{}", err), + ) } }