chore: yanked nodeinfo because i need some changes

will eventually PR them in, here's the source
https://codeberg.org/thefederationinfo/nodeinfo-rs
This commit is contained in:
əlemi 2023-10-18 00:37:52 +02:00
parent 806ff114ba
commit 341a0a77aa
Signed by: alemi
GPG key ID: A4895B84D311642C
4 changed files with 200 additions and 0 deletions

View file

@ -12,6 +12,7 @@ chrono = "0.4.31"
clap = { version = "4.4.6", features = ["derive"] }
derive_more = "0.99.17"
lazy_static = "1.4.0"
# nodeinfo = { git = "https://codeberg.org/thefederationinfo/nodeinfo-rs.git" }
reqwest = { version = "0.11.20", features = ["json"] }
sea-orm = { version = "0.12.3", features = ["runtime-tokio-native-tls", "sqlx-sqlite", "sqlx-postgres"] }
serde = { version = "1.0.188", features = ["derive"] }

50
src/nodeinfo/fetcher.rs Normal file
View file

@ -0,0 +1,50 @@
use std::time::Duration;
use serde::Deserialize;
use crate::nodeinfo::model::NodeInfoOwned;
#[derive(Debug, Clone, Deserialize)]
struct WellKnownNodeInfo {
links: Vec<WellKnownNodeInfoRef>
}
#[derive(Debug, Clone, Deserialize)]
struct WellKnownNodeInfoRef {
rel: String,
href: String,
}
pub async fn node_info(domain: &str) -> Result<NodeInfoOwned, reqwest::Error> {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(5))
.build()?;
let well_known : WellKnownNodeInfo = client
.get(format!("https://{}/.well-known/nodeinfo", domain))
.send()
.await?
.json()
.await?;
let (_, best_version) = well_known.links.iter()
.filter_map(|x| Some((parse_nodeinfo_spec_version(&x.rel)?, x)))
.max_by(|a, b| a.0.cmp(&b.0))
.expect("no versions available");
client.get(&best_version.href)
.send()
.await?
.json()
.await
}
fn parse_nodeinfo_spec_version(schema: &str) -> Option<i32> {
match schema { // TODO this silently filters out unsupported new versions
"http://nodeinfo.diaspora.software/ns/schema/1.0" => Some(1000),
"http://nodeinfo.diaspora.software/ns/schema/1.1" => Some(1010),
"http://nodeinfo.diaspora.software/ns/schema/2.0" => Some(2000),
"http://nodeinfo.diaspora.software/ns/schema/2.1" => Some(2010),
_ => None,
}
}

2
src/nodeinfo/mod.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod model;
pub mod fetcher;

147
src/nodeinfo/model.rs Normal file
View file

@ -0,0 +1,147 @@
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
/// Node metadata for version detection only used for deserialization.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, PartialOrd, derive_more::Display)]
pub(crate) enum NodeVersion {
V1_0 = 1000,
V1_1 = 1001,
V2_0 = 2000,
V2_1 = 2001,
}
pub type NodeInfo<'a> = NodeInfoInternal<&'a str>;
pub type NodeInfoOwned = NodeInfoInternal<String>;
/// Node metadata about a server running in the fediverse.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct NodeInfoInternal<T> {
pub version: NodeVersion,
pub software: Software<T>,
#[serde(default)]
pub protocols: Vec<T>,
#[serde(default)]
pub services: Services<T>,
#[serde(rename = "openRegistrations", default)]
pub open_registrations: bool,
#[serde(default)]
pub usage: Usage,
#[serde(default)]
pub metadata: Map<String, Value>,
}
/// Node legacy metadata about a server running in the federation, only used for deserialization.
#[derive(Debug, PartialEq, Deserialize)]
pub(crate) struct LegacyNodeInfo<'a> {
version: NodeVersion,
software: Software<&'a str>,
#[serde(default)]
services: Services<&'a str>,
#[serde(default)]
protocols: Services<&'a str>,
#[serde(rename = "openRegistrations", default)]
open_registrations: bool,
#[serde(default)]
usage: Usage,
#[serde(default)]
metadata: Map<String, Value>,
}
/// Software contains infos about software running on the node.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Software<T> {
pub name: T,
pub version: T,
pub repository: Option<T>,
pub homepage: Option<T>,
}
/// Services tell about third party sites the node can connect to or interact with.
#[derive(Debug, PartialEq, Default, Serialize, Deserialize)]
pub struct Services<T> {
pub inbound: Vec<T>,
pub outbound: Vec<T>,
}
/// Usage statistics for the node
#[derive(Debug, PartialEq, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Usage {
pub users: Users,
pub local_posts: Option<i64>,
pub local_comments: Option<i64>,
}
// UsersUsage are statistics about the users of the node.
#[derive(Debug, PartialEq, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Users {
pub total: Option<i64>,
pub active_halfyear: Option<i64>,
pub active_month: Option<i64>,
}
impl<'a> Software<&'a str> {
fn to_owned(&self) -> Software<String> {
Software {
name: self.name.to_string(),
version: self.version.to_string(),
repository: self.repository.map(ToString::to_string),
homepage: self.homepage.map(ToString::to_string),
}
}
}
impl<'a> Services<&'a str> {
fn to_owned(&self) -> Services<String> {
Services {
inbound: self.inbound.iter().map(ToString::to_string).collect(),
outbound: self.outbound.iter().map(ToString::to_string).collect(),
}
}
}
impl NodeInfo<'_> {
/// Converts all internal `&str`s into `String`s.
pub fn to_owned(&self) -> NodeInfoOwned {
NodeInfoOwned {
version: self.version.to_string(),
software: self.software.to_owned(),
protocols: self.protocols.iter().map(ToString::to_string).collect(),
services: self.services.to_owned(),
open_registrations: self.open_registrations,
usage: self.usage.clone(),
metadata: self.metadata.clone(),
}
}
}
impl<'a> From<LegacyNodeInfo<'a>> for NodeInfo<'a> {
fn from(other: LegacyNodeInfo<'a>) -> Self {
let mut combined_protocols: Vec<&'a str> = other
.protocols
.inbound
.into_iter()
.chain(other.protocols.outbound)
.collect();
combined_protocols.sort();
combined_protocols.dedup();
NodeInfo {
version: other.version,
software: other.software,
services: other.services,
protocols: combined_protocols,
open_registrations: other.open_registrations,
usage: other.usage,
metadata: other.metadata,
}
}
}
impl<'a> From<LegacyNodeInfo<'a>> for NodeInfoOwned {
fn from(value: LegacyNodeInfo<'a>) -> Self {
NodeInfo::from(value).to_owned()
}
}