ap types and nodeinfo
This commit is contained in:
@@ -0,0 +1,56 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use axum::Json;
|
||||||
|
use axum::extract::State;
|
||||||
|
use axum::response::Response;
|
||||||
|
|
||||||
|
use crate::state::AppState;
|
||||||
|
|
||||||
|
pub async fn well_known(
|
||||||
|
State(state): State<Arc<AppState>>,
|
||||||
|
) -> Result<Json<serde_json::Value>, Response> {
|
||||||
|
let ap = state.ap.as_ref().ok_or_else(|| {
|
||||||
|
(axum::http::StatusCode::NOT_FOUND, "not found").into_response()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(Json(serde_json::json!({
|
||||||
|
"links": [{
|
||||||
|
"rel": "http://nodeinfo/2.1",
|
||||||
|
"href": format!("https://{}/nodeinfo/2.1", ap.domain),
|
||||||
|
}]
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
use axum::response::IntoResponse;
|
||||||
|
|
||||||
|
pub async fn nodeinfo(
|
||||||
|
State(state): State<Arc<AppState>>,
|
||||||
|
) -> Result<Json<serde_json::Value>, Response> {
|
||||||
|
let ap = state.ap.as_ref().ok_or_else(|| {
|
||||||
|
(axum::http::StatusCode::NOT_FOUND, "not found").into_response()
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let dbi = state.db.lock().await;
|
||||||
|
let users: i64 = dbi.db.query_row("SELECT COUNT(*) FROM sessions", [], |r| r.get(0)).unwrap_or(0);
|
||||||
|
let puzzles: i64 = dbi.db.query_row("SELECT COUNT(*) FROM puzzles", [], |r| r.get(0)).unwrap_or(0);
|
||||||
|
|
||||||
|
Ok(Json(serde_json::json!({
|
||||||
|
"version": "2.1",
|
||||||
|
"software": {
|
||||||
|
"name": "cruciverb",
|
||||||
|
"version": env!("CARGO_PKG_VERSION"),
|
||||||
|
"repository": "https://git.lashman.live/lashman/cruciverb",
|
||||||
|
},
|
||||||
|
"protocols": ["activitypub"],
|
||||||
|
"services": { "inbound": [], "outbound": ["atom1.0", "rss2.0"] },
|
||||||
|
"openRegistrations": true,
|
||||||
|
"usage": {
|
||||||
|
"users": { "total": users },
|
||||||
|
"localPosts": puzzles,
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"nodeName": format!("cruciverb @ {}", ap.domain),
|
||||||
|
"nodeDescription": "self-hosted crossword puzzles",
|
||||||
|
"features": ["daily_puzzles", "community_clues", "puzzle_editor", "cryptic_mode"],
|
||||||
|
},
|
||||||
|
})))
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub const PUBLIC: &str = "https://www.w3.org/ns/activitystreams#Public";
|
||||||
|
pub const CONTEXT: &str = "https://www.w3.org/ns/activitystreams";
|
||||||
|
pub const SECURITY_CONTEXT: &str = "https://w3id.org/security/v1";
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Actor {
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
pub context: serde_json::Value,
|
||||||
|
pub id: String,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub actor_type: String,
|
||||||
|
#[serde(rename = "preferredUsername")]
|
||||||
|
pub preferred_username: String,
|
||||||
|
pub name: String,
|
||||||
|
pub summary: String,
|
||||||
|
pub inbox: String,
|
||||||
|
pub outbox: String,
|
||||||
|
pub followers: String,
|
||||||
|
#[serde(rename = "publicKey")]
|
||||||
|
pub public_key: ActorPublicKey,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub endpoints: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ActorPublicKey {
|
||||||
|
pub id: String,
|
||||||
|
pub owner: String,
|
||||||
|
#[serde(rename = "publicKeyPem")]
|
||||||
|
pub public_key_pem: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Activity {
|
||||||
|
#[serde(rename = "@context", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub context: Option<serde_json::Value>,
|
||||||
|
pub id: String,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub activity_type: String,
|
||||||
|
pub actor: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub published: Option<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub to: Option<Vec<String>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub cc: Option<Vec<String>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub object: Option<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Note {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub note_type: String,
|
||||||
|
pub id: String,
|
||||||
|
#[serde(rename = "attributedTo")]
|
||||||
|
pub attributed_to: String,
|
||||||
|
pub content: String,
|
||||||
|
pub published: String,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub url: Option<String>,
|
||||||
|
pub to: Vec<String>,
|
||||||
|
pub cc: Vec<String>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub tag: Option<Vec<serde_json::Value>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub attachment: Option<Vec<serde_json::Value>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct OrderedCollection {
|
||||||
|
#[serde(rename = "@context")]
|
||||||
|
pub context: String,
|
||||||
|
pub id: String,
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub collection_type: String,
|
||||||
|
#[serde(rename = "totalItems")]
|
||||||
|
pub total_items: usize,
|
||||||
|
#[serde(rename = "orderedItems")]
|
||||||
|
pub ordered_items: Vec<serde_json::Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct WebFingerResponse {
|
||||||
|
pub subject: String,
|
||||||
|
pub links: Vec<WebFingerLink>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct WebFingerLink {
|
||||||
|
pub rel: String,
|
||||||
|
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub link_type: Option<String>,
|
||||||
|
pub href: String,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user