Merge remote-tracking branch 'origin/max' into max

This commit is contained in:
Maxime Augier 2024-07-03 13:59:57 +02:00
commit 3bdffd6144
21 changed files with 432 additions and 429 deletions

488
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,11 @@
[package]
name = "ap-relay"
description = "A simple activitypub relay"
version = "0.3.106"
version = "0.3.108"
authors = ["asonix <asonix@asonix.dog>"]
license = "AGPL-3.0"
readme = "README.md"
repository = "https://git.asonix.dog/asonix/ap-relay"
repository = "https://git.asonix.dog/asonix/relay"
keywords = ["activitypub", "relay"]
edition = "2021"
build = "src/build.rs"
@ -21,8 +21,7 @@ default = []
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
actix-web = { version = "4.4.0", default-features = false, features = ["compress-brotli", "compress-gzip", "rustls-0_21"] }
actix-web = { version = "4.4.0", default-features = false, features = ["compress-brotli", "compress-gzip", "rustls-0_22"] }
actix-webfinger = { version = "0.5.0", default-features = false }
activitystreams = "0.7.0-alpha.25"
activitystreams-ext = "0.1.0-alpha.3"
@ -31,7 +30,8 @@ async-cpupool = "0.2.0"
bcrypt = "0.15"
base64 = "0.21"
clap = { version = "4.0.0", features = ["derive"] }
config = "0.13.0"
color-eyre = "0.6.2"
config = { version = "0.14.0", default-features = false, features = ["toml", "json", "yaml"] }
console-subscriber = { version = "0.2", optional = true }
dashmap = "5.1.0"
dotenv = "0.15.0"
@ -57,11 +57,13 @@ reqwest-tracing = "0.4.5"
ring = "0.17.5"
rsa = { version = "0.9" }
rsa-magic-public-key = "0.8.0"
rustls = "0.21.0"
rustls-pemfile = "1.0.1"
rustls = "0.22.0"
rustls-channel-resolver = "0.2.0"
rustls-pemfile = "2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sled = "0.34.7"
streem = "0.2.0"
teloxide = { version = "0.12.0", default-features = false, features = [
"ctrlc_handler",
"macros",
@ -80,10 +82,9 @@ tracing-subscriber = { version = "0.3", features = [
] }
tokio = { version = "1", features = ["full", "tracing"] }
uuid = { version = "1", features = ["v4", "serde"] }
streem = "0.2.0"
[dependencies.background-jobs]
version = "0.17.0"
version = "0.18.0"
default-features = false
features = ["error-logging", "metrics", "tokio"]
@ -101,7 +102,7 @@ features = ["middleware", "ring"]
version = "0.7.9"
[build-dependencies]
anyhow = "1.0"
color-eyre = "0.6.2"
dotenv = "0.15.0"
ructe = { version = "0.17.0", features = ["sass", "mime03"] }
toml = "0.8.0"

View File

@ -5,7 +5,7 @@
rustPlatform.buildRustPackage {
pname = "relay";
version = "0.3.106";
version = "0.3.108";
src = ./.;
cargoLock.lockFile = ./Cargo.lock;

View File

@ -21,7 +21,7 @@ fn git_info() {
}
}
fn version_info() -> Result<(), anyhow::Error> {
fn version_info() -> color_eyre::Result<()> {
let cargo_toml = Path::new(&std::env::var("CARGO_MANIFEST_DIR")?).join("Cargo.toml");
let mut file = File::open(cargo_toml)?;
@ -42,7 +42,7 @@ fn version_info() -> Result<(), anyhow::Error> {
Ok(())
}
fn main() -> Result<(), anyhow::Error> {
fn main() -> color_eyre::Result<()> {
dotenv::dotenv().ok();
git_info();

View File

@ -12,9 +12,8 @@ use activitystreams::{
};
use config::Environment;
use http_signature_normalization_actix::{digest::ring::Sha256, prelude::VerifyDigest};
use rustls::{Certificate, PrivateKey};
use rustls::sign::CertifiedKey;
use std::{
io::BufReader,
net::{IpAddr, SocketAddr},
path::PathBuf,
};
@ -312,43 +311,34 @@ impl Config {
Some((config.addr, config.port).into())
}
pub(crate) fn open_keys(&self) -> Result<Option<(Vec<Certificate>, PrivateKey)>, Error> {
pub(crate) async fn open_keys(&self) -> Result<Option<CertifiedKey>, Error> {
let tls = if let Some(tls) = &self.tls {
tls
} else {
tracing::warn!("No TLS config present");
tracing::info!("No TLS config present");
return Ok(None);
};
let mut certs_reader = BufReader::new(std::fs::File::open(&tls.cert)?);
let certs = rustls_pemfile::certs(&mut certs_reader)?;
let certs_bytes = tokio::fs::read(&tls.cert).await?;
let certs =
rustls_pemfile::certs(&mut certs_bytes.as_slice()).collect::<Result<Vec<_>, _>>()?;
if certs.is_empty() {
tracing::warn!("No certs read from certificate file");
return Ok(None);
}
let mut key_reader = BufReader::new(std::fs::File::open(&tls.key)?);
let key = rustls_pemfile::read_one(&mut key_reader)?;
let certs = certs.into_iter().map(Certificate).collect();
let key = if let Some(key) = key {
match key {
rustls_pemfile::Item::RSAKey(der) => PrivateKey(der),
rustls_pemfile::Item::PKCS8Key(der) => PrivateKey(der),
rustls_pemfile::Item::ECKey(der) => PrivateKey(der),
_ => {
tracing::warn!("Unknown key format: {:?}", key);
return Ok(None);
}
}
let key_bytes = tokio::fs::read(&tls.key).await?;
let key = if let Some(key) = rustls_pemfile::private_key(&mut key_bytes.as_slice())? {
key
} else {
tracing::warn!("Failed to read private key");
return Ok(None);
};
Ok(Some((certs, key)))
let key = rustls::crypto::ring::sign::any_supported_type(&key)?;
Ok(Some(CertifiedKey::new(certs, key)))
}
pub(crate) fn footer_blurb(&self) -> Option<crate::templates::Html<String>> {

View File

@ -4,54 +4,82 @@ use actix_web::{
http::StatusCode,
HttpResponse,
};
use background_jobs::BoxError;
use color_eyre::eyre::Error as Report;
use http_signature_normalization_reqwest::SignError;
use std::{convert::Infallible, fmt::Debug, io};
use std::{convert::Infallible, io, sync::Arc};
use tokio::task::JoinError;
use tracing_error::SpanTrace;
#[derive(Clone)]
struct ArcKind {
kind: Arc<ErrorKind>,
}
impl std::fmt::Debug for ArcKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f)
}
}
impl std::fmt::Display for ArcKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.kind.fmt(f)
}
}
impl std::error::Error for ArcKind {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.kind.source()
}
}
pub(crate) struct Error {
context: String,
kind: ErrorKind,
kind: ArcKind,
display: Box<str>,
debug: Box<str>,
}
impl Error {
fn kind(&self) -> &ErrorKind {
&self.kind.kind
}
pub(crate) fn is_breaker(&self) -> bool {
matches!(self.kind, ErrorKind::Breaker)
matches!(self.kind(), ErrorKind::Breaker)
}
pub(crate) fn is_not_found(&self) -> bool {
matches!(self.kind, ErrorKind::Status(_, StatusCode::NOT_FOUND))
matches!(self.kind(), ErrorKind::Status(_, StatusCode::NOT_FOUND))
}
pub(crate) fn is_bad_request(&self) -> bool {
matches!(self.kind, ErrorKind::Status(_, StatusCode::BAD_REQUEST))
matches!(self.kind(), ErrorKind::Status(_, StatusCode::BAD_REQUEST))
}
pub(crate) fn is_gone(&self) -> bool {
matches!(self.kind, ErrorKind::Status(_, StatusCode::GONE))
matches!(self.kind(), ErrorKind::Status(_, StatusCode::GONE))
}
pub(crate) fn is_malformed_json(&self) -> bool {
matches!(self.kind, ErrorKind::Json(_))
matches!(self.kind(), ErrorKind::Json(_))
}
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{:?}", self.kind)
f.write_str(&self.debug)
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.kind)?;
std::fmt::Display::fmt(&self.context, f)
f.write_str(&self.display)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.kind.source()
self.kind().source()
}
}
@ -60,25 +88,36 @@ where
ErrorKind: From<T>,
{
fn from(error: T) -> Self {
let kind = ArcKind {
kind: Arc::new(ErrorKind::from(error)),
};
let report = Report::new(kind.clone());
let display = format!("{report}");
let debug = format!("{report:?}");
Error {
context: SpanTrace::capture().to_string(),
kind: error.into(),
kind,
display: Box::from(display),
debug: Box::from(debug),
}
}
}
#[derive(Debug, thiserror::Error)]
pub(crate) enum ErrorKind {
#[error("Error queueing job, {0}")]
Queue(anyhow::Error),
#[error("Error in extractor")]
Extractor(#[from] crate::extractors::ErrorKind),
#[error("Error in configuration, {0}")]
#[error("Error queueing job")]
Queue(#[from] BoxError),
#[error("Error in configuration")]
Config(#[from] config::ConfigError),
#[error("Couldn't parse key, {0}")]
#[error("Couldn't parse key")]
Pkcs8(#[from] rsa::pkcs8::Error),
#[error("Couldn't encode public key, {0}")]
#[error("Couldn't encode public key")]
Spki(#[from] rsa::pkcs8::spki::Error),
#[error("Couldn't sign request")]
@ -87,33 +126,36 @@ pub(crate) enum ErrorKind {
#[error("Couldn't make request")]
Reqwest(#[from] reqwest::Error),
#[error("Couldn't build client")]
#[error("Couldn't make request")]
ReqwestMiddleware(#[from] reqwest_middleware::Error),
#[error("Couldn't parse IRI, {0}")]
#[error("Couldn't parse IRI")]
ParseIri(#[from] activitystreams::iri_string::validate::Error),
#[error("Couldn't normalize IRI, {0}")]
#[error("Couldn't normalize IRI")]
NormalizeIri(#[from] std::collections::TryReserveError),
#[error("Couldn't perform IO, {0}")]
#[error("Couldn't perform IO")]
Io(#[from] io::Error),
#[error("Couldn't sign string, {0}")]
Rsa(rsa::errors::Error),
#[error("Couldn't use db, {0}")]
#[error("Couldn't use db")]
Sled(#[from] sled::Error),
#[error("Couldn't do the json thing, {0}")]
#[error("Couldn't do the json thing")]
Json(#[from] serde_json::Error),
#[error("Couldn't sign request, {0}")]
#[error("Couldn't sign request")]
Sign(#[from] SignError),
#[error("Couldn't sign digest")]
Signature(#[from] rsa::signature::Error),
#[error("Couldn't prepare TLS private key")]
PrepareKey(#[from] rustls::Error),
#[error("Couldn't verify signature")]
VerifySignature,
@ -144,10 +186,10 @@ pub(crate) enum ErrorKind {
#[error("Wrong ActivityPub kind, {0}")]
Kind(String),
#[error("Too many CPUs, {0}")]
#[error("Too many CPUs")]
CpuCount(#[from] std::num::TryFromIntError),
#[error("{0}")]
#[error("Host mismatch")]
HostMismatch(#[from] CheckError),
#[error("Couldn't flush buffer")]
@ -201,7 +243,7 @@ pub(crate) enum ErrorKind {
impl ResponseError for Error {
fn status_code(&self) -> StatusCode {
match self.kind {
match self.kind() {
ErrorKind::NotAllowed(_) | ErrorKind::WrongActor(_) | ErrorKind::BadActor(_, _) => {
StatusCode::FORBIDDEN
}
@ -221,7 +263,7 @@ impl ResponseError for Error {
.insert_header(("Content-Type", "application/activity+json"))
.body(
serde_json::to_string(&serde_json::json!({
"error": self.kind.to_string(),
"error": self.kind().to_string(),
}))
.unwrap_or_else(|_| "{}".to_string()),
)

View File

@ -1,19 +1,15 @@
use actix_web::{
dev::Payload,
error::ParseError,
http::{
header::{from_one_raw_str, Header, HeaderName, HeaderValue, TryIntoHeaderValue},
StatusCode,
},
http::header::{from_one_raw_str, Header, HeaderName, HeaderValue, TryIntoHeaderValue},
web::Data,
FromRequest, HttpMessage, HttpRequest, HttpResponse, ResponseError,
FromRequest, HttpMessage, HttpRequest,
};
use bcrypt::{BcryptError, DEFAULT_COST};
use http_signature_normalization_actix::{prelude::InvalidHeaderValue, Canceled, Spawn};
use std::{convert::Infallible, str::FromStr, time::Instant};
use tracing_error::SpanTrace;
use crate::{db::Db, future::LocalBoxFuture, spawner::Spawner};
use crate::{db::Db, error::Error, future::LocalBoxFuture, spawner::Spawner};
#[derive(Clone)]
pub(crate) struct AdminConfig {
@ -83,74 +79,42 @@ impl Admin {
}
}
#[derive(Debug, thiserror::Error)]
#[error("Failed authentication")]
pub(crate) struct Error {
context: String,
#[source]
kind: ErrorKind,
}
impl Error {
fn invalid() -> Self {
Error {
context: SpanTrace::capture().to_string(),
kind: ErrorKind::Invalid,
}
Error::from(ErrorKind::Invalid)
}
fn missing_config() -> Self {
Error {
context: SpanTrace::capture().to_string(),
kind: ErrorKind::MissingConfig,
}
Error::from(ErrorKind::MissingConfig)
}
fn missing_db() -> Self {
Error {
context: SpanTrace::capture().to_string(),
kind: ErrorKind::MissingDb,
}
Error::from(ErrorKind::MissingDb)
}
fn missing_spawner() -> Self {
Error {
context: SpanTrace::capture().to_string(),
kind: ErrorKind::MissingSpawner,
}
Error::from(ErrorKind::MissingSpawner)
}
fn bcrypt_verify(e: BcryptError) -> Self {
Error {
context: SpanTrace::capture().to_string(),
kind: ErrorKind::BCryptVerify(e),
}
Error::from(ErrorKind::BCryptVerify(e))
}
fn bcrypt_hash(e: BcryptError) -> Self {
Error {
context: SpanTrace::capture().to_string(),
kind: ErrorKind::BCryptHash(e),
}
Error::from(ErrorKind::BCryptHash(e))
}
fn parse_header(e: ParseError) -> Self {
Error {
context: SpanTrace::capture().to_string(),
kind: ErrorKind::ParseHeader(e),
}
Error::from(ErrorKind::ParseHeader(e))
}
fn canceled(_: Canceled) -> Self {
Error {
context: SpanTrace::capture().to_string(),
kind: ErrorKind::Canceled,
}
Error::from(ErrorKind::Canceled)
}
}
#[derive(Debug, thiserror::Error)]
enum ErrorKind {
pub(crate) enum ErrorKind {
#[error("Invalid API Token")]
Invalid,
@ -176,20 +140,6 @@ enum ErrorKind {
ParseHeader(#[source] ParseError),
}
impl ResponseError for Error {
fn status_code(&self) -> StatusCode {
match self.kind {
ErrorKind::Invalid | ErrorKind::ParseHeader(_) => StatusCode::BAD_REQUEST,
_ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
fn error_response(&self) -> HttpResponse {
HttpResponse::build(self.status_code())
.json(serde_json::json!({ "msg": self.kind.to_string() }))
}
}
impl FromRequest for Admin {
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>;

View File

@ -64,12 +64,13 @@ fn generate_announce(
impl Job for Announce {
type State = JobState;
type Future = BoxFuture<'static, anyhow::Result<()>>;
type Error = Error;
type Future = BoxFuture<'static, Result<(), Self::Error>>;
const NAME: &'static str = "relay::jobs::apub::Announce";
const QUEUE: &'static str = "apub";
fn run(self, state: Self::State) -> Self::Future {
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
Box::pin(self.perform(state))
}
}

View File

@ -113,12 +113,13 @@ fn generate_accept_follow(
impl Job for Follow {
type State = JobState;
type Future = BoxFuture<'static, anyhow::Result<()>>;
type Error = Error;
type Future = BoxFuture<'static, Result<(), Self::Error>>;
const NAME: &'static str = "relay::jobs::apub::Follow";
const QUEUE: &'static str = "apub";
fn run(self, state: Self::State) -> Self::Future {
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
Box::pin(self.perform(state))
}
}

View File

@ -49,12 +49,13 @@ impl Forward {
impl Job for Forward {
type State = JobState;
type Future = BoxFuture<'static, anyhow::Result<()>>;
type Error = Error;
type Future = BoxFuture<'static, Result<(), Self::Error>>;
const NAME: &'static str = "relay::jobs::apub::Forward";
const QUEUE: &'static str = "apub";
fn run(self, state: Self::State) -> Self::Future {
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
Box::pin(self.perform(state))
}
}

View File

@ -35,12 +35,13 @@ impl Reject {
impl Job for Reject {
type State = JobState;
type Future = BoxFuture<'static, anyhow::Result<()>>;
type Error = Error;
type Future = BoxFuture<'static, Result<(), Self::Error>>;
const NAME: &'static str = "relay::jobs::apub::Reject";
const QUEUE: &'static str = "apub";
fn run(self, state: Self::State) -> Self::Future {
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
Box::pin(self.perform(state))
}
}

View File

@ -50,12 +50,13 @@ impl Undo {
impl Job for Undo {
type State = JobState;
type Future = BoxFuture<'static, anyhow::Result<()>>;
type Error = Error;
type Future = BoxFuture<'static, Result<(), Self::Error>>;
const NAME: &'static str = "relay::jobs::apub::Undo";
const QUEUE: &'static str = "apub";
fn run(self, state: Self::State) -> Self::Future {
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
Box::pin(self.perform(state))
}
}

View File

@ -87,13 +87,14 @@ fn to_contact(contact: AcceptedActors) -> Option<(String, String, IriString, Iri
impl Job for QueryContact {
type State = JobState;
type Future = BoxFuture<'static, anyhow::Result<()>>;
type Error = Error;
type Future = BoxFuture<'static, Result<(), Self::Error>>;
const NAME: &'static str = "relay::jobs::QueryContact";
const QUEUE: &'static str = "maintenance";
fn run(self, state: Self::State) -> Self::Future {
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
Box::pin(self.perform(state))
}
}

View File

@ -35,7 +35,7 @@ impl Deliver {
}
#[tracing::instrument(name = "Deliver", skip(state))]
async fn permform(self, state: JobState) -> Result<(), Error> {
async fn perform(self, state: JobState) -> Result<(), Error> {
if let Err(e) = state
.state
.requests
@ -58,13 +58,14 @@ impl Deliver {
impl Job for Deliver {
type State = JobState;
type Future = BoxFuture<'static, anyhow::Result<()>>;
type Error = Error;
type Future = BoxFuture<'static, Result<(), Self::Error>>;
const NAME: &'static str = "relay::jobs::Deliver";
const QUEUE: &'static str = "deliver";
const BACKOFF: Backoff = Backoff::Exponential(8);
fn run(self, state: Self::State) -> Self::Future {
Box::pin(async move { self.permform(state).await.map_err(Into::into) })
Box::pin(self.perform(state))
}
}

View File

@ -47,12 +47,13 @@ impl DeliverMany {
impl Job for DeliverMany {
type State = JobState;
type Future = BoxFuture<'static, anyhow::Result<()>>;
type Error = Error;
type Future = BoxFuture<'static, Result<(), Self::Error>>;
const NAME: &'static str = "relay::jobs::DeliverMany";
const QUEUE: &'static str = "deliver";
fn run(self, state: Self::State) -> Self::Future {
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
Box::pin(self.perform(state))
}
}

View File

@ -167,13 +167,14 @@ impl QueryInstance {
impl Job for QueryInstance {
type State = JobState;
type Future = BoxFuture<'static, anyhow::Result<()>>;
type Error = Error;
type Future = BoxFuture<'static, Result<(), Self::Error>>;
const NAME: &'static str = "relay::jobs::QueryInstance";
const QUEUE: &'static str = "maintenance";
fn run(self, state: Self::State) -> Self::Future {
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
Box::pin(self.perform(state))
}
}

View File

@ -106,13 +106,14 @@ impl QueryNodeinfo {
impl Job for QueryNodeinfo {
type State = JobState;
type Future = BoxFuture<'static, anyhow::Result<()>>;
type Error = Error;
type Future = BoxFuture<'static, Result<(), Self::Error>>;
const NAME: &'static str = "relay::jobs::QueryNodeinfo";
const QUEUE: &'static str = "maintenance";
fn run(self, state: Self::State) -> Self::Future {
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
Box::pin(self.perform(state))
}
}

View File

@ -25,12 +25,13 @@ impl Listeners {
impl Job for Listeners {
type State = JobState;
type Future = BoxFuture<'static, anyhow::Result<()>>;
type Error = Error;
type Future = BoxFuture<'static, Result<(), Self::Error>>;
const NAME: &'static str = "relay::jobs::Listeners";
const QUEUE: &'static str = "maintenance";
fn run(self, state: Self::State) -> Self::Future {
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
Box::pin(self.perform(state))
}
}

View File

@ -15,13 +15,14 @@ impl RecordLastOnline {
impl Job for RecordLastOnline {
type State = JobState;
type Future = BoxFuture<'static, anyhow::Result<()>>;
type Error = Error;
type Future = BoxFuture<'static, Result<(), Self::Error>>;
const NAME: &'static str = "relay::jobs::RecordLastOnline";
const QUEUE: &'static str = "maintenance";
const BACKOFF: Backoff = Backoff::Linear(1);
fn run(self, state: Self::State) -> Self::Future {
Box::pin(async move { self.perform(state).await.map_err(Into::into) })
Box::pin(self.perform(state))
}
}

View File

@ -21,7 +21,7 @@ use tokio::task::JoinHandle;
use tracing_actix_web::TracingLogger;
use tracing_error::ErrorLayer;
use tracing_log::LogTracer;
use tracing_subscriber::{filter::Targets, fmt::format::FmtSpan, layer::SubscriberExt, Layer};
use tracing_subscriber::{filter::Targets, layer::SubscriberExt, Layer};
mod admin;
mod apub;
@ -56,16 +56,15 @@ use self::{
fn init_subscriber(
software_name: &'static str,
opentelemetry_url: Option<&IriString>,
) -> Result<(), anyhow::Error> {
) -> color_eyre::Result<()> {
LogTracer::init()?;
color_eyre::install()?;
let targets: Targets = std::env::var("RUST_LOG")
.unwrap_or_else(|_| "warn,actix_web=debug,actix_server=debug,tracing_actix_web=info".into())
.unwrap_or_else(|_| "info".into())
.parse()?;
let format_layer = tracing_subscriber::fmt::layer()
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
.with_filter(targets.clone());
let format_layer = tracing_subscriber::fmt::layer().with_filter(targets.clone());
#[cfg(feature = "console")]
let console_layer = ConsoleLayer::builder()
@ -142,7 +141,7 @@ fn build_client(
}
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
async fn main() -> color_eyre::Result<()> {
dotenv::dotenv().ok();
let config = Config::build()?;
@ -168,30 +167,30 @@ async fn main() -> Result<(), anyhow::Error> {
.add_recorder(recorder)
.add_recorder(collector.clone())
.build();
metrics::set_global_recorder(recorder).map_err(|e| anyhow::anyhow!("{e}"))?;
metrics::set_global_recorder(recorder).map_err(|e| color_eyre::eyre::eyre!("{e}"))?;
} else {
collector.install()?;
}
tracing::warn!("Opening DB");
tracing::info!("Opening DB");
let db = Db::build(&config)?;
tracing::warn!("Building caches");
tracing::info!("Building caches");
let actors = ActorCache::new(db.clone());
let media = MediaCache::new(db.clone());
server_main(db, actors, media, collector, config).await?;
tracing::warn!("Application exit");
tracing::info!("Application exit");
Ok(())
}
fn client_main(config: Config, args: Args) -> JoinHandle<Result<(), anyhow::Error>> {
fn client_main(config: Config, args: Args) -> JoinHandle<color_eyre::Result<()>> {
tokio::spawn(do_client_main(config, args))
}
async fn do_client_main(config: Config, args: Args) -> Result<(), anyhow::Error> {
async fn do_client_main(config: Config, args: Args) -> color_eyre::Result<()> {
let client = build_client(
&config.user_agent(),
config.client_timeout(),
@ -282,14 +281,14 @@ async fn server_main(
media: MediaCache,
collector: MemoryCollector,
config: Config,
) -> Result<(), anyhow::Error> {
) -> color_eyre::Result<()> {
let client = build_client(
&config.user_agent(),
config.client_timeout(),
config.proxy_config(),
)?;
tracing::warn!("Creating state");
tracing::info!("Creating state");
let (signature_threads, verify_threads) = match config.signature_threads() {
0 | 1 => (1, 1),
@ -309,15 +308,19 @@ async fn server_main(
let state = State::build(db.clone(), key_id, sign_spawner.clone(), client).await?;
if let Some((token, admin_handle)) = config.telegram_info() {
tracing::warn!("Creating telegram handler");
tracing::info!("Creating telegram handler");
telegram::start(admin_handle.to_owned(), db.clone(), token);
}
let keys = config.open_keys()?;
let cert_resolver = config
.open_keys()
.await?
.map(rustls_channel_resolver::channel::<32>);
let bind_address = config.bind_address();
let sign_spawner2 = sign_spawner.clone();
let verify_spawner2 = verify_spawner.clone();
let config2 = config.clone();
let server = HttpServer::new(move || {
let job_server =
create_workers(state.clone(), actors.clone(), media.clone(), config.clone())
@ -387,27 +390,42 @@ async fn server_main(
)
});
if let Some((certs, key)) = keys {
tracing::warn!("Binding to {}:{} with TLS", bind_address.0, bind_address.1);
if let Some((cert_tx, cert_rx)) = cert_resolver {
let handle = tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(30));
interval.tick().await;
loop {
interval.tick().await;
match config2.open_keys().await {
Ok(Some(key)) => cert_tx.update(key),
Ok(None) => tracing::warn!("Missing TLS keys"),
Err(e) => tracing::error!("Failed to read TLS keys {e}"),
}
}
});
tracing::info!("Binding to {}:{} with TLS", bind_address.0, bind_address.1);
let server_config = ServerConfig::builder()
.with_safe_default_cipher_suites()
.with_safe_default_kx_groups()
.with_safe_default_protocol_versions()?
.with_no_client_auth()
.with_single_cert(certs, key)?;
.with_cert_resolver(cert_rx);
server
.bind_rustls_021(bind_address, server_config)?
.bind_rustls_0_22(bind_address, server_config)?
.run()
.await?;
handle.abort();
let _ = handle.await;
} else {
tracing::warn!("Binding to {}:{}", bind_address.0, bind_address.1);
tracing::info!("Binding to {}:{}", bind_address.0, bind_address.1);
server.bind(bind_address)?.run().await?;
}
sign_spawner2.close().await;
verify_spawner2.close().await;
tracing::warn!("Server closed");
tracing::info!("Server closed");
Ok(())
}

View File

@ -8,7 +8,7 @@ pub(crate) struct Spawner {
}
impl Spawner {
pub(crate) fn build(name: &'static str, threads: u16) -> anyhow::Result<Self> {
pub(crate) fn build(name: &'static str, threads: u16) -> color_eyre::Result<Self> {
let pool = CpuPool::configure()
.name(name)
.max_threads(threads)