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-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-16 22:44:08 +02:00
|
|
|
#[derive(Debug, Clone)]
|
2024-04-17 22:00:40 +02:00
|
|
|
struct Config {}
|
2024-04-16 22:44:08 +02:00
|
|
|
|
2024-04-16 19:53:24 +02:00
|
|
|
#[derive(Debug, Clone)]
|
2024-01-26 21:01:43 +01:00
|
|
|
struct State {
|
2024-04-09 19:48:18 +02:00
|
|
|
database: Arc<Database>,
|
2024-04-17 22:00:40 +02:00
|
|
|
config: Arc<Config>,
|
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-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-17 22:00:40 +02:00
|
|
|
let config = Config {};
|
2024-04-16 22:44:08 +02:00
|
|
|
|
2024-04-16 19:53:24 +02:00
|
|
|
let state: State = State {
|
2024-04-15 01:07:15 +02:00
|
|
|
database: new_database,
|
2024-04-17 22:00:40 +02:00
|
|
|
config: Arc::new(config),
|
2024-04-16 19:53:24 +02:00
|
|
|
};
|
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-16 19:53:24 +02:00
|
|
|
info!("Main thread shutdown..");
|
2024-04-15 01:07:15 +02:00
|
|
|
|
2024-01-26 21:01:43 +01:00
|
|
|
Ok(())
|
|
|
|
|
}
|