feat: nodeinfo (well barebones but kinda)
This commit is contained in:
parent
3e81574783
commit
69f0239764
3 changed files with 86 additions and 4 deletions
|
@ -21,3 +21,5 @@ tracing = "0.1.40"
|
||||||
tracing-subscriber = "0.3.18"
|
tracing-subscriber = "0.3.18"
|
||||||
uuid = { version = "1.8.0", features = ["v4"] }
|
uuid = { version = "1.8.0", features = ["v4"] }
|
||||||
jrd = "0.1"
|
jrd = "0.1"
|
||||||
|
# nodeinfo = "0.0.2" # the version on crates.io doesn't re-export necessary types to build the struct!!!
|
||||||
|
nodeinfo = { git = "https://codeberg.org/thefederationinfo/nodeinfo-rs", rev = "e865094804" }
|
||||||
|
|
|
@ -1,8 +1,86 @@
|
||||||
use axum::{extract::{Query, State}, http::StatusCode, response::{IntoResponse, Response}};
|
use axum::{extract::{Path, Query, State}, http::StatusCode, response::{IntoResponse, Response}, Json};
|
||||||
use jrd::{JsonResourceDescriptor, JsonResourceDescriptorLink};
|
use jrd::{JsonResourceDescriptor, JsonResourceDescriptorLink};
|
||||||
use sea_orm::EntityTrait;
|
use sea_orm::{EntityTrait, PaginatorTrait};
|
||||||
|
|
||||||
|
use crate::{model, server::Context};
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
pub struct NodeInfoDiscovery {
|
||||||
|
pub links: Vec<NodeInfoDiscoveryRel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
pub struct NodeInfoDiscoveryRel {
|
||||||
|
pub rel: String,
|
||||||
|
pub href: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn nodeinfo_discovery(State(ctx): State<Context>) -> Json<NodeInfoDiscovery> {
|
||||||
|
Json(NodeInfoDiscovery {
|
||||||
|
links: vec![
|
||||||
|
NodeInfoDiscoveryRel {
|
||||||
|
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0".into(),
|
||||||
|
href: format!("{}/nodeinfo/2.0.json", ctx.base()),
|
||||||
|
},
|
||||||
|
NodeInfoDiscoveryRel {
|
||||||
|
rel: "http://nodeinfo.diaspora.software/ns/schema/2.1".into(),
|
||||||
|
href: format!("{}/nodeinfo/2.1.json", ctx.base()),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn nodeinfo(State(ctx): State<Context>, Path(version): Path<String>) -> Result<Json<nodeinfo::NodeInfoOwned>, StatusCode> {
|
||||||
|
// TODO it's unsustainable to count these every time, especially comments since it's a complex
|
||||||
|
// filter! keep these numbers caches somewhere, maybe db, so that we can just look them up
|
||||||
|
let total_users = model::user::Entity::find().count(ctx.db()).await.ok();
|
||||||
|
let total_posts = None;
|
||||||
|
let total_comments = None;
|
||||||
|
let (software, version) = match version.as_str() {
|
||||||
|
"2.0.json" => (
|
||||||
|
nodeinfo::types::Software {
|
||||||
|
name: "μpub".to_string(),
|
||||||
|
version: Some(env!("CARGO_PKG_VERSION").into()),
|
||||||
|
repository: None,
|
||||||
|
homepage: None,
|
||||||
|
},
|
||||||
|
"2.0".to_string()
|
||||||
|
),
|
||||||
|
"2.1.json" => (
|
||||||
|
nodeinfo::types::Software {
|
||||||
|
name: "μpub".to_string(),
|
||||||
|
version: Some(env!("CARGO_PKG_VERSION").into()),
|
||||||
|
repository: Some("https://git.alemi.dev/upub.git/".into()),
|
||||||
|
homepage: None,
|
||||||
|
},
|
||||||
|
"2.1".to_string()
|
||||||
|
),
|
||||||
|
_ => return Err(StatusCode::NOT_IMPLEMENTED),
|
||||||
|
};
|
||||||
|
Ok(Json(
|
||||||
|
nodeinfo::NodeInfoOwned {
|
||||||
|
version,
|
||||||
|
software,
|
||||||
|
open_registrations: false,
|
||||||
|
protocols: vec!["activitypub".into()],
|
||||||
|
services: nodeinfo::types::Services {
|
||||||
|
inbound: vec![],
|
||||||
|
outbound: vec![],
|
||||||
|
},
|
||||||
|
usage: nodeinfo::types::Usage {
|
||||||
|
local_posts: total_posts,
|
||||||
|
local_comments: total_comments,
|
||||||
|
users: Some(nodeinfo::types::Users {
|
||||||
|
active_month: None,
|
||||||
|
active_halfyear: None,
|
||||||
|
total: total_users.map(|x| x as i64),
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
metadata: serde_json::Map::default(),
|
||||||
|
}
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
use crate::{server::Context, model};
|
|
||||||
|
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize)]
|
||||||
pub struct WebfingerQuery {
|
pub struct WebfingerQuery {
|
||||||
|
@ -12,7 +90,7 @@ pub struct WebfingerQuery {
|
||||||
pub struct JsonRD<T>(pub T);
|
pub struct JsonRD<T>(pub T);
|
||||||
impl<T: serde::Serialize> IntoResponse for JsonRD<T> {
|
impl<T: serde::Serialize> IntoResponse for JsonRD<T> {
|
||||||
fn into_response(self) -> Response {
|
fn into_response(self) -> Response {
|
||||||
([("Content-Type", "application/jrd+json")], axum::Json(self.0)).into_response()
|
([("Content-Type", "application/jrd+json")], Json(self.0)).into_response()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,6 +80,8 @@ pub async fn serve(db: DatabaseConnection, domain: String) {
|
||||||
// .well-known and discovery
|
// .well-known and discovery
|
||||||
.route("/.well-known/webfinger", get(ap::well_known::webfinger))
|
.route("/.well-known/webfinger", get(ap::well_known::webfinger))
|
||||||
.route("/.well-known/host-meta", get(ap::well_known::host_meta))
|
.route("/.well-known/host-meta", get(ap::well_known::host_meta))
|
||||||
|
.route("/.well-known/nodeinfo", get(ap::well_known::nodeinfo_discovery))
|
||||||
|
.route("/nodeinfo/:version", get(ap::well_known::nodeinfo))
|
||||||
// actor routes
|
// actor routes
|
||||||
.route("/users/:id", get(ap::user::view))
|
.route("/users/:id", get(ap::user::view))
|
||||||
.route("/users/:id/inbox", post(ap::user::inbox))
|
.route("/users/:id/inbox", post(ap::user::inbox))
|
||||||
|
|
Loading…
Reference in a new issue