]> Untitled Git - lemmy.git/blob - crates/routes/src/nodeinfo.rs
1841b15917ab100bce1201e37d2a4f8dae3ff976
[lemmy.git] / crates / routes / src / nodeinfo.rs
1 use actix_web::{error::ErrorBadRequest, *};
2 use anyhow::anyhow;
3 use lemmy_api_common::utils::blocking;
4 use lemmy_db_views::structs::SiteView;
5 use lemmy_utils::{error::LemmyError, version};
6 use lemmy_websocket::LemmyContext;
7 use serde::{Deserialize, Serialize};
8 use url::Url;
9
10 pub fn config(cfg: &mut web::ServiceConfig) {
11   cfg
12     .route("/nodeinfo/2.0.json", web::get().to(node_info))
13     .route("/.well-known/nodeinfo", web::get().to(node_info_well_known));
14 }
15
16 async fn node_info_well_known(
17   context: web::Data<LemmyContext>,
18 ) -> Result<HttpResponse, LemmyError> {
19   let node_info = NodeInfoWellKnown {
20     links: vec![NodeInfoWellKnownLinks {
21       rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.0")?,
22       href: Url::parse(&format!(
23         "{}/nodeinfo/2.0.json",
24         &context.settings().get_protocol_and_hostname(),
25       ))?,
26     }],
27   };
28   Ok(HttpResponse::Ok().json(node_info))
29 }
30
31 async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Error> {
32   let site_view = blocking(context.pool(), SiteView::read_local)
33     .await?
34     .map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?;
35
36   let protocols = if context.settings().federation.enabled {
37     vec!["activitypub".to_string()]
38   } else {
39     vec![]
40   };
41
42   let json = NodeInfo {
43     version: "2.0".to_string(),
44     software: NodeInfoSoftware {
45       name: "lemmy".to_string(),
46       version: version::VERSION.to_string(),
47     },
48     protocols,
49     usage: NodeInfoUsage {
50       users: NodeInfoUsers {
51         total: site_view.counts.users,
52         active_halfyear: site_view.counts.users_active_half_year,
53         active_month: site_view.counts.users_active_month,
54       },
55       local_posts: site_view.counts.posts,
56       local_comments: site_view.counts.comments,
57     },
58     open_registrations: site_view.site.open_registration,
59   };
60
61   Ok(HttpResponse::Ok().json(json))
62 }
63
64 #[derive(Serialize, Deserialize, Debug)]
65 struct NodeInfoWellKnown {
66   pub links: Vec<NodeInfoWellKnownLinks>,
67 }
68
69 #[derive(Serialize, Deserialize, Debug)]
70 struct NodeInfoWellKnownLinks {
71   pub rel: Url,
72   pub href: Url,
73 }
74
75 #[derive(Serialize, Deserialize, Debug)]
76 #[serde(rename_all = "camelCase")]
77 struct NodeInfo {
78   pub version: String,
79   pub software: NodeInfoSoftware,
80   pub protocols: Vec<String>,
81   pub usage: NodeInfoUsage,
82   pub open_registrations: bool,
83 }
84
85 #[derive(Serialize, Deserialize, Debug)]
86 struct NodeInfoSoftware {
87   pub name: String,
88   pub version: String,
89 }
90
91 #[derive(Serialize, Deserialize, Debug)]
92 #[serde(rename_all = "camelCase")]
93 struct NodeInfoUsage {
94   pub users: NodeInfoUsers,
95   pub local_posts: i64,
96   pub local_comments: i64,
97 }
98
99 #[derive(Serialize, Deserialize, Debug)]
100 #[serde(rename_all = "camelCase")]
101 struct NodeInfoUsers {
102   pub total: i64,
103   pub active_halfyear: i64,
104   pub active_month: i64,
105 }