1 use actix_web::{error::ErrorBadRequest, web, Error, HttpResponse, Result};
3 use lemmy_api_common::context::LemmyContext;
4 use lemmy_db_schema::RegistrationMode;
5 use lemmy_db_views::structs::SiteView;
7 cache_header::{cache_1hour, cache_3days},
11 use serde::{Deserialize, Serialize};
14 pub fn config(cfg: &mut web::ServiceConfig) {
18 web::get().to(node_info).wrap(cache_1hour()),
21 "/.well-known/nodeinfo",
22 web::get().to(node_info_well_known).wrap(cache_3days()),
26 async fn node_info_well_known(
27 context: web::Data<LemmyContext>,
28 ) -> Result<HttpResponse, LemmyError> {
29 let node_info = NodeInfoWellKnown {
30 links: vec![NodeInfoWellKnownLinks {
31 rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.0")?,
32 href: Url::parse(&format!(
33 "{}/nodeinfo/2.0.json",
34 &context.settings().get_protocol_and_hostname(),
38 Ok(HttpResponse::Ok().json(node_info))
41 async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Error> {
42 let site_view = SiteView::read_local(&mut context.pool())
44 .map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?;
46 let protocols = if site_view.local_site.federation_enabled {
47 Some(vec!["activitypub".to_string()])
51 let open_registrations = Some(site_view.local_site.registration_mode == RegistrationMode::Open);
53 version: Some("2.0".to_string()),
54 software: Some(NodeInfoSoftware {
55 name: Some("lemmy".to_string()),
56 version: Some(version::VERSION.to_string()),
59 usage: Some(NodeInfoUsage {
60 users: Some(NodeInfoUsers {
61 total: Some(site_view.counts.users),
62 active_halfyear: Some(site_view.counts.users_active_half_year),
63 active_month: Some(site_view.counts.users_active_month),
65 local_posts: Some(site_view.counts.posts),
66 local_comments: Some(site_view.counts.comments),
71 Ok(HttpResponse::Ok().json(json))
74 #[derive(Serialize, Deserialize, Debug)]
75 struct NodeInfoWellKnown {
76 pub links: Vec<NodeInfoWellKnownLinks>,
79 #[derive(Serialize, Deserialize, Debug)]
80 struct NodeInfoWellKnownLinks {
85 #[derive(Serialize, Deserialize, Debug, Default)]
86 #[serde(rename_all = "camelCase", default)]
88 pub version: Option<String>,
89 pub software: Option<NodeInfoSoftware>,
90 pub protocols: Option<Vec<String>>,
91 pub usage: Option<NodeInfoUsage>,
92 pub open_registrations: Option<bool>,
95 #[derive(Serialize, Deserialize, Debug, Default)]
97 pub struct NodeInfoSoftware {
98 pub name: Option<String>,
99 pub version: Option<String>,
102 #[derive(Serialize, Deserialize, Debug, Default)]
103 #[serde(rename_all = "camelCase", default)]
104 pub struct NodeInfoUsage {
105 pub users: Option<NodeInfoUsers>,
106 pub local_posts: Option<i64>,
107 pub local_comments: Option<i64>,
110 #[derive(Serialize, Deserialize, Debug, Default)]
111 #[serde(rename_all = "camelCase", default)]
112 pub struct NodeInfoUsers {
113 pub total: Option<i64>,
114 pub active_halfyear: Option<i64>,
115 pub active_month: Option<i64>,