From 94a5f3a3cd3cbc70456087879a970710e5450cdd Mon Sep 17 00:00:00 2001 From: silverpill Date: Mon, 6 Mar 2023 19:49:45 +0000 Subject: [PATCH] Implement NodeInfo 2.1 --- CHANGELOG.md | 1 + src/main.rs | 3 +- src/nodeinfo/types.rs | 117 ++++++++++++++++++++++++++++++++---------- src/nodeinfo/views.rs | 27 ++++++++-- 4 files changed, 117 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index be1191e..f122ffc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Added `emojis` field to Mastodon API Account entity. - Support audio attachments. - Added CLI command for viewing unreachable actors. +- Implemented NodeInfo 2.1. ### Changed diff --git a/src/main.rs b/src/main.rs index aa4102f..c15ca9c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -169,8 +169,9 @@ async fn main() -> std::io::Result<()> { .service(activitypub::emoji_view) .service(activitypub::tag_view) .service(atom_scope()) - .service(nodeinfo::get_nodeinfo) + .service(nodeinfo::get_nodeinfo_jrd) .service(nodeinfo::get_nodeinfo_2_0) + .service(nodeinfo::get_nodeinfo_2_1) .service(web_client::profile_page_redirect()) .service(web_client::post_page_redirect()) .service( diff --git a/src/nodeinfo/types.rs b/src/nodeinfo/types.rs index b2ada63..e70f100 100644 --- a/src/nodeinfo/types.rs +++ b/src/nodeinfo/types.rs @@ -4,16 +4,56 @@ use serde::Serialize; use mitra_config::{Config, RegistrationType, MITRA_VERSION}; +const MITRA_NAME: &str = "mitra"; +const MITRA_REPOSITORY: &str = "https://codeberg.org/silverpill/mitra"; +const ATOM_SERVICE: &str = "atom1.0"; +const ACTIVITYPUB_PROTOCOL: &str = "activitypub"; + #[derive(Serialize)] -struct Software { +struct Software20 { name: String, version: String, } +impl Default for Software20 { + fn default() -> Self { + Self { + name: MITRA_NAME.to_string(), + version: MITRA_VERSION.to_string(), + } + } +} + +#[derive(Serialize)] +struct Software21 { + name: String, + version: String, + repository: String, +} + +impl Default for Software21 { + fn default() -> Self { + Self { + name: MITRA_NAME.to_string(), + version: MITRA_VERSION.to_string(), + repository: MITRA_REPOSITORY.to_string(), + } + } +} + #[derive(Serialize)] struct Services { - inbound: Vec, - outbound: Vec, + inbound: Vec<&'static str>, + outbound: Vec<&'static str>, +} + +impl Default for Services { + fn default() -> Self { + Self { + inbound: vec![], + outbound: vec![ATOM_SERVICE], + } + } } #[derive(Serialize)] @@ -35,12 +75,25 @@ struct Metadata { node_description: String, } +impl Metadata { + fn new(config: &Config) -> Self { + Self { + node_name: config.instance_title.clone(), + node_description: config.instance_short_description.clone(), + } + } +} + +fn has_open_registrations(config: &Config) -> bool { + config.registration.registration_type != RegistrationType::Invite +} + #[derive(Serialize)] #[serde(rename_all = "camelCase")] pub struct NodeInfo20 { - version: String, - software: Software, - protocols: Vec, + version: &'static str, + software: Software20, + protocols: Vec<&'static str>, services: Services, open_registrations: bool, usage: Usage, @@ -49,28 +102,40 @@ pub struct NodeInfo20 { impl NodeInfo20 { pub fn new(config: &Config, usage: Usage) -> Self { - let software = Software { - name: "mitra".to_string(), - version: MITRA_VERSION.to_string(), - }; - let services = Services { - inbound: vec![], - outbound: vec!["atom1.0".to_string()], - }; - let metadata = Metadata { - node_name: config.instance_title.clone(), - node_description: config.instance_short_description.clone(), - }; Self { - version: "2.0".to_string(), - software, - protocols: vec!["activitypub".to_string()], - services, - open_registrations: - config.registration.registration_type != - RegistrationType::Invite, + version: "2.0", + software: Software20::default(), + protocols: vec![ACTIVITYPUB_PROTOCOL], + services: Services::default(), + open_registrations: has_open_registrations(config), usage, - metadata, + metadata: Metadata::new(config), + } + } +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct NodeInfo21 { + version: &'static str, + software: Software21, + protocols: Vec<&'static str>, + services: Services, + open_registrations: bool, + usage: Usage, + metadata: Metadata, +} + +impl NodeInfo21 { + pub fn new(config: &Config, usage: Usage) -> Self { + Self { + version: "2.1", + software: Software21::default(), + protocols: vec![ACTIVITYPUB_PROTOCOL], + services: Services::default(), + open_registrations: has_open_registrations(config), + usage, + metadata: Metadata::new(config), } } } diff --git a/src/nodeinfo/views.rs b/src/nodeinfo/views.rs index f901b9f..a468e4f 100644 --- a/src/nodeinfo/views.rs +++ b/src/nodeinfo/views.rs @@ -11,22 +11,29 @@ use crate::webfinger::types::{ JsonResourceDescriptor, }; use super::helpers::get_usage; -use super::types::NodeInfo20; +use super::types::{NodeInfo20, NodeInfo21}; #[get("/.well-known/nodeinfo")] -pub async fn get_nodeinfo( +pub async fn get_nodeinfo_jrd( config: web::Data, ) -> Result { let nodeinfo_2_0_url = format!("{}/nodeinfo/2.0", config.instance_url()); - let link = Link { + let nodeinfo_2_0_link = Link { rel: "http://nodeinfo.diaspora.software/ns/schema/2.0".to_string(), media_type: None, href: Some(nodeinfo_2_0_url), properties: Default::default(), }; + let nodeinfo_2_1_url = format!("{}/nodeinfo/2.1", config.instance_url()); + let nodeinfo_2_1_link = Link { + rel: "http://nodeinfo.diaspora.software/ns/schema/2.1".to_string(), + media_type: None, + href: Some(nodeinfo_2_1_url), + properties: Default::default(), + }; let jrd = JsonResourceDescriptor { subject: config.instance_url(), - links: vec![link], + links: vec![nodeinfo_2_0_link, nodeinfo_2_1_link], }; let response = HttpResponse::Ok().json(jrd); Ok(response) @@ -43,3 +50,15 @@ pub async fn get_nodeinfo_2_0( let response = HttpResponse::Ok().json(nodeinfo); Ok(response) } + +#[get("/nodeinfo/2.1")] +pub async fn get_nodeinfo_2_1( + config: web::Data, + db_pool: web::Data, +) -> Result { + let db_client = &**get_database_client(&db_pool).await?; + let usage = get_usage(db_client).await?; + let nodeinfo = NodeInfo21::new(&config, usage); + let response = HttpResponse::Ok().json(nodeinfo); + Ok(response) +}