activitypub/src/main.rs

152 lines
4.3 KiB
Rust
Raw Normal View History

2024-04-09 19:48:18 +02:00
use activitypub_federation::config::{FederationConfig, FederationMiddleware};
use actix_web::{get, http::KeepAlive, middleware, web, App, Error, HttpResponse, HttpServer};
use actix_web_prom::PrometheusMetricsBuilder;
use clap::Parser;
use database::Database;
use http::{http_get_user, http_post_user_inbox, webfinger};
use objects::person::DbUser;
2024-04-15 01:07:15 +02:00
use redis::{AsyncCommands, Client, ErrorKind, RedisError, RedisResult};
use redis_macros::{FromRedisValue, ToRedisArgs};
2024-01-26 21:01:43 +01:00
use serde::{Deserialize, Serialize};
2024-04-09 19:48:18 +02:00
use std::{
2024-04-09 19:55:07 +02:00
collections::HashMap,
env,
net::ToSocketAddrs,
sync::{Arc, Mutex},
2024-04-09 19:48:18 +02:00
};
2024-04-09 19:55:07 +02:00
use tokio::signal;
2024-04-15 01:07:15 +02:00
use tracing::info;
2024-04-09 19:48:18 +02:00
mod activities;
2024-04-09 19:55:07 +02:00
mod database;
2024-04-09 19:48:18 +02:00
mod error;
mod http;
2024-04-09 19:55:07 +02:00
mod objects;
mod utils;
2024-01-26 21:01:43 +01:00
2024-04-15 01:07:15 +02:00
#[derive(Debug, Clone, Serialize, Deserialize, FromRedisValue, ToRedisArgs)]
2024-01-26 21:01:43 +01:00
struct State {
2024-04-09 19:48:18 +02:00
database: Arc<Database>,
2024-01-26 21:01:43 +01:00
}
#[derive(Debug, Serialize, Deserialize)]
struct Response {
health: bool,
}
2024-04-09 19:48:18 +02:00
#[derive(Parser, Debug)]
#[clap(author = "April John", version, about)]
/// Application configuration
struct Args {
/// whether to be verbose
#[arg(short = 'v')]
verbose: bool,
/// optional parse arg for config file
#[arg()]
config_file: Option<String>,
}
2024-01-26 21:01:43 +01:00
#[get("/")]
async fn index(_: web::Data<State>) -> actix_web::Result<HttpResponse, Error> {
Ok(HttpResponse::Ok().json(Response { health: true }))
}
2024-04-09 19:48:18 +02:00
const DOMAIN: &str = "example.com";
const LOCAL_USER_NAME: &str = "example";
2024-01-26 21:01:43 +01:00
#[actix_web::main]
2024-04-09 19:48:18 +02:00
async fn main() -> actix_web::Result<(), anyhow::Error> {
2024-01-26 21:01:43 +01:00
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
let server_url = env::var("LISTEN").unwrap_or("127.0.0.1:8080".to_string());
2024-04-15 01:07:15 +02:00
let redis_url = env::var("REDIS_URL").unwrap_or("redis://localhost:6379/".to_string());
let redis_client = redis::Client::open(redis_url)?;
let mut con = redis_client
.get_multiplexed_async_connection()
.await
.map_err(|_| {
RedisError::from((
ErrorKind::InvalidClientConfig,
"Cannot connect to redis. Try starting a redis-server process or container.",
))
})?;
2024-01-26 21:01:43 +01:00
2024-04-09 19:55:07 +02:00
let local_user = DbUser::new(
env::var("FEDERATED_DOMAIN")
.unwrap_or(DOMAIN.to_string())
.as_str(),
env::var("LOCAL_USER_NAME")
.unwrap_or(LOCAL_USER_NAME.to_string())
.as_str(),
)
.unwrap();
2024-04-09 19:48:18 +02:00
2024-04-15 01:07:15 +02:00
let new_database = Arc::new(Database {
2024-04-09 19:48:18 +02:00
users: Mutex::new(vec![local_user]),
});
2024-01-26 21:01:43 +01:00
2024-04-15 01:07:15 +02:00
let state: State = con.get("ap-layer-db").await.unwrap_or(State {
database: new_database,
});
2024-01-26 21:01:43 +01:00
2024-04-09 19:48:18 +02:00
let data = FederationConfig::builder()
.domain(env::var("FEDERATED_DOMAIN").expect("FEDERATED_DOMAIN must be set"))
.app_data(state.clone().database)
2024-04-09 19:55:07 +02:00
.build()
.await?;
2024-04-09 19:48:18 +02:00
let mut labels = HashMap::new();
2024-04-09 19:55:07 +02:00
labels.insert(
"domain".to_string(),
env::var("FEDERATED_DOMAIN")
.expect("FEDERATED_DOMAIN must be set")
.to_string(),
);
labels.insert(
"name".to_string(),
env::var("LOCAL_USER_NAME")
.expect("LOCAL_USER_NAME must be set")
.to_string(),
);
2024-04-09 19:48:18 +02:00
let prometheus = PrometheusMetricsBuilder::new("api")
.endpoint("/metrics")
.const_labels(labels)
.build()
.unwrap();
let http_server = HttpServer::new(move || {
2024-01-26 21:01:43 +01:00
App::new()
.app_data(web::Data::new(state.clone()))
.wrap(middleware::Logger::default()) // enable logger
2024-04-09 19:48:18 +02:00
.wrap(prometheus.clone())
.wrap(FederationMiddleware::new(data.clone()))
.route("/{user}", web::get().to(http_get_user))
.route("/{user}/inbox", web::post().to(http_post_user_inbox))
.route("/.well-known/webfinger", web::get().to(webfinger))
2024-01-26 21:01:43 +01:00
.service(index)
})
.bind(&server_url)?
2024-04-09 19:48:18 +02:00
.workers(num_cpus::get())
.shutdown_timeout(20)
.keep_alive(KeepAlive::Os)
.run();
tokio::spawn(http_server);
match signal::ctrl_c().await {
2024-04-09 19:55:07 +02:00
Ok(()) => {}
2024-04-09 19:48:18 +02:00
Err(err) => {
eprintln!("Unable to listen for shutdown signal: {}", err);
// we also shut down in case of error
2024-04-09 19:55:07 +02:00
}
2024-04-09 19:48:18 +02:00
}
2024-01-26 21:01:43 +01:00
2024-04-15 01:07:15 +02:00
info!("memory-saving 'db' in redis..");
2024-04-15 01:17:11 +02:00
con.set("ap-layer-db", &state).await?;
2024-04-15 01:07:15 +02:00
2024-01-26 21:01:43 +01:00
Ok(())
}