1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#![warn(missing_docs)]
use actix_web::middleware::Logger;
use actix_web::{web, App, HttpServer};
use tokio::task;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::prelude::*;
use tracing_subscriber::{filter, fmt, EnvFilter};
mod activitypub;
mod args;
mod database;
mod frontend;
mod http_api;
mod remote;
mod snowflake;
use database::Database;
use http_api::CanonicalServerUrl;
use remote::RemoteAccessor;
#[tokio::main]
pub async fn main() {
let args: args::Args = argh::from_env();
let log_filter = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new("info"))
.unwrap();
let event_format = fmt::format().without_time().compact();
let fmt_layer = fmt::layer().event_format(event_format);
let sqlx_filter = filter::filter_fn(|metadata| {
metadata.module_path() != Some("sqlx::query") || metadata.level() <= &LevelFilter::WARN
});
tracing_subscriber::registry()
.with(fmt_layer)
.with(log_filter)
.with(sqlx_filter)
.init();
tracing::debug!("Starting up notedealer...");
fn make_static<T>(t: T) -> &'static T {
Box::leak(Box::new(t))
}
let db = match Database::new(&args.database_url).await {
Ok(db) => make_static(db),
Err(err) => {
tracing::error!("Cannot open db: {err}");
std::process::exit(1);
}
};
let addr = &args.bind_addr;
let canonical_server_url = CanonicalServerUrl(args.activitypub_url);
let factory = move || {
let canonical_server_url = canonical_server_url.clone();
App::new()
.app_data(web::Data::new(db))
.app_data(web::Data::new(RemoteAccessor::default()))
.app_data(web::Data::new(canonical_server_url))
.route("/", web::get().to(frontend::html!("index.html")))
.route("/favicon.png", web::get().to(frontend::png!("favicon.png")))
.service(http_api::users::service())
.service(http_api::webfinger::get)
.wrap(Logger::default())
};
let Ok(server) = HttpServer::new(factory).bind(addr) else {
tracing::error!("Cannot bind HTTP server to {addr}, is it already bound?");
std::process::exit(1);
};
let delivery_loop_task_set = task::LocalSet::new();
let delivery_loop = delivery_loop_task_set.run_until(async {
activitypub::inbox_queue::delivery_loop(db).await;
});
let server_name = env!("CARGO_PKG_NAME");
tracing::info!("{server_name} HTTP server bound to: http://{addr}/");
if let (Err(err), _) = tokio::join!(server.run(), delivery_loop) {
tracing::error!("Fatal error in {server_name}: {err}");
std::process::exit(1);
}
}