From 510207b101b3354a3c3bf290438672956516417f Mon Sep 17 00:00:00 2001 From: naskya Date: Mon, 6 May 2024 04:23:38 +0900 Subject: [PATCH] refactor (backend-rs): separate nodeinfo generator and schema --- .../src/service/nodeinfo/generate.rs | 140 ++++++++++++++++++ .../backend-rs/src/service/nodeinfo/mod.rs | 2 + .../{nodeinfo.rs => nodeinfo/schema.rs} | 138 ----------------- 3 files changed, 142 insertions(+), 138 deletions(-) create mode 100644 packages/backend-rs/src/service/nodeinfo/generate.rs create mode 100644 packages/backend-rs/src/service/nodeinfo/mod.rs rename packages/backend-rs/src/service/{nodeinfo.rs => nodeinfo/schema.rs} (55%) diff --git a/packages/backend-rs/src/service/nodeinfo/generate.rs b/packages/backend-rs/src/service/nodeinfo/generate.rs new file mode 100644 index 0000000000..168b13ba1b --- /dev/null +++ b/packages/backend-rs/src/service/nodeinfo/generate.rs @@ -0,0 +1,140 @@ +use crate::config::CONFIG; +use crate::database::cache; +use crate::database::db_conn; +use crate::misc::meta::fetch_meta; +use crate::model::entity::{note, user}; +use crate::service::nodeinfo::schema::*; +use sea_orm::{ColumnTrait, DbErr, EntityTrait, PaginatorTrait, QueryFilter}; +use serde_json::json; +use std::collections::HashMap; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Database error: {0}")] + DbErr(#[from] DbErr), + #[error("Cache error: {0}")] + CacheErr(#[from] cache::Error), + #[error("Failed to serialize nodeinfo to JSON: {0}")] + JsonErr(#[from] serde_json::Error), +} + +async fn statistics() -> Result<(u64, u64, u64, u64), DbErr> { + let db = db_conn().await?; + + let now = chrono::Local::now().naive_local(); + const MONTH: chrono::TimeDelta = chrono::Duration::seconds(2592000000); + const HALF_YEAR: chrono::TimeDelta = chrono::Duration::seconds(15552000000); + + let local_users = user::Entity::find() + .filter(user::Column::Host.is_null()) + .count(db); + let local_active_halfyear = user::Entity::find() + .filter(user::Column::Host.is_null()) + .filter(user::Column::LastActiveDate.gt(now - HALF_YEAR)) + .count(db); + let local_active_month = user::Entity::find() + .filter(user::Column::Host.is_null()) + .filter(user::Column::LastActiveDate.gt(now - MONTH)) + .count(db); + let local_posts = note::Entity::find() + .filter(note::Column::UserHost.is_null()) + .count(db); + + tokio::try_join!( + local_users, + local_active_halfyear, + local_active_month, + local_posts + ) +} + +async fn get_new_nodeinfo_2_1() -> Result { + let (local_users, local_active_halfyear, local_active_month, local_posts) = + statistics().await?; + let meta = fetch_meta(true).await?; + let metadata = HashMap::from([ + ( + "nodeName".to_string(), + json!(meta.name.unwrap_or(CONFIG.host.clone())), + ), + ("nodeDescription".to_string(), json!(meta.description)), + ("repositoryUrl".to_string(), json!(meta.repository_url)), + ( + "enableLocalTimeline".to_string(), + json!(!meta.disable_local_timeline), + ), + ( + "enableRecommendedTimeline".to_string(), + json!(!meta.disable_recommended_timeline), + ), + ( + "enableGlobalTimeline".to_string(), + json!(!meta.disable_global_timeline), + ), + ( + "enableGuestTimeline".to_string(), + json!(meta.enable_guest_timeline), + ), + ("maintainerName".to_string(), json!(meta.maintainer_name)), + ("maintainerEmail".to_string(), json!(meta.maintainer_email)), + ("proxyAccountName".to_string(), json!(meta.proxy_account_id)), + ( + "themeColor".to_string(), + json!(meta.theme_color.unwrap_or("#31748f".to_string())), + ), + ]); + + Ok(Nodeinfo21 { + version: "2.1".to_string(), + software: Software21 { + name: "firefish".to_string(), + version: CONFIG.version.clone(), + repository: Some(meta.repository_url), + homepage: Some("https://firefish.dev/firefish/firefish".to_string()), + }, + protocols: vec![Protocol::Activitypub], + services: Services { + inbound: vec![], + outbound: vec![Outbound::Atom1, Outbound::Rss2], + }, + open_registrations: !meta.disable_registration, + usage: Usage { + users: Users { + total: Some(local_users), + active_halfyear: Some(local_active_halfyear), + active_month: Some(local_active_month), + }, + local_posts: Some(local_posts), + local_comments: None, + }, + metadata, + }) +} + +pub async fn nodeinfo_2_1() -> Result { + const NODEINFO_2_1_CACHE_KEY: &str = "nodeinfo_2_1"; + + let cached = cache::get::(NODEINFO_2_1_CACHE_KEY)?; + + if let Some(nodeinfo) = cached { + Ok(nodeinfo) + } else { + let nodeinfo = get_new_nodeinfo_2_1().await?; + cache::set(NODEINFO_2_1_CACHE_KEY, &nodeinfo, 60 * 60)?; + Ok(nodeinfo) + } +} + +pub async fn nodeinfo_2_0() -> Result { + Ok(nodeinfo_2_1().await?.into()) +} + +#[crate::export(js_name = "nodeinfo_2_1")] +pub async fn nodeinfo_2_1_as_json() -> Result { + Ok(serde_json::to_value(nodeinfo_2_1().await?)?) +} + +#[crate::export(js_name = "nodeinfo_2_0")] +pub async fn nodeinfo_2_0_as_json() -> Result { + Ok(serde_json::to_value(nodeinfo_2_0().await?)?) +} diff --git a/packages/backend-rs/src/service/nodeinfo/mod.rs b/packages/backend-rs/src/service/nodeinfo/mod.rs new file mode 100644 index 0000000000..563a8046d6 --- /dev/null +++ b/packages/backend-rs/src/service/nodeinfo/mod.rs @@ -0,0 +1,2 @@ +pub mod generate; +pub mod schema; diff --git a/packages/backend-rs/src/service/nodeinfo.rs b/packages/backend-rs/src/service/nodeinfo/schema.rs similarity index 55% rename from packages/backend-rs/src/service/nodeinfo.rs rename to packages/backend-rs/src/service/nodeinfo/schema.rs index 5760d7162d..51f2ebf687 100644 --- a/packages/backend-rs/src/service/nodeinfo.rs +++ b/packages/backend-rs/src/service/nodeinfo/schema.rs @@ -1,11 +1,4 @@ -use crate::config::CONFIG; -use crate::database::cache; -use crate::database::db_conn; -use crate::misc::meta::fetch_meta; -use crate::model::entity::{note, user}; -use sea_orm::{ColumnTrait, DbErr, EntityTrait, PaginatorTrait, QueryFilter}; use serde::{Deserialize, Serialize}; -use serde_json::json; use std::collections::HashMap; // TODO: I want to use these macros but they don't work with rmp_serde @@ -194,134 +187,3 @@ impl From for Nodeinfo20 { } } } - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error("Database error: {0}")] - DbErr(#[from] DbErr), - #[error("Cache error: {0}")] - CacheErr(#[from] cache::Error), - #[error("Failed to serialize nodeinfo to JSON: {0}")] - JsonErr(#[from] serde_json::Error), -} - -async fn statistics() -> Result<(u64, u64, u64, u64), DbErr> { - let db = db_conn().await?; - - let now = chrono::Local::now().naive_local(); - const MONTH: chrono::TimeDelta = chrono::Duration::seconds(2592000000); - const HALF_YEAR: chrono::TimeDelta = chrono::Duration::seconds(15552000000); - - let local_users = user::Entity::find() - .filter(user::Column::Host.is_null()) - .count(db); - let local_active_halfyear = user::Entity::find() - .filter(user::Column::Host.is_null()) - .filter(user::Column::LastActiveDate.gt(now - HALF_YEAR)) - .count(db); - let local_active_month = user::Entity::find() - .filter(user::Column::Host.is_null()) - .filter(user::Column::LastActiveDate.gt(now - MONTH)) - .count(db); - let local_posts = note::Entity::find() - .filter(note::Column::UserHost.is_null()) - .count(db); - - tokio::try_join!( - local_users, - local_active_halfyear, - local_active_month, - local_posts - ) -} - -async fn get_new_nodeinfo_2_1() -> Result { - let (local_users, local_active_halfyear, local_active_month, local_posts) = - statistics().await?; - let meta = fetch_meta(true).await?; - let metadata = HashMap::from([ - ( - "nodeName".to_string(), - json!(meta.name.unwrap_or(CONFIG.host.clone())), - ), - ("nodeDescription".to_string(), json!(meta.description)), - ("repositoryUrl".to_string(), json!(meta.repository_url)), - ( - "enableLocalTimeline".to_string(), - json!(!meta.disable_local_timeline), - ), - ( - "enableRecommendedTimeline".to_string(), - json!(!meta.disable_recommended_timeline), - ), - ( - "enableGlobalTimeline".to_string(), - json!(!meta.disable_global_timeline), - ), - ( - "enableGuestTimeline".to_string(), - json!(meta.enable_guest_timeline), - ), - ("maintainerName".to_string(), json!(meta.maintainer_name)), - ("maintainerEmail".to_string(), json!(meta.maintainer_email)), - ("proxyAccountName".to_string(), json!(meta.proxy_account_id)), - ( - "themeColor".to_string(), - json!(meta.theme_color.unwrap_or("#31748f".to_string())), - ), - ]); - - Ok(Nodeinfo21 { - version: "2.1".to_string(), - software: Software21 { - name: "firefish".to_string(), - version: CONFIG.version.clone(), - repository: Some(meta.repository_url), - homepage: Some("https://firefish.dev/firefish/firefish".to_string()), - }, - protocols: vec![Protocol::Activitypub], - services: Services { - inbound: vec![], - outbound: vec![Outbound::Atom1, Outbound::Rss2], - }, - open_registrations: !meta.disable_registration, - usage: Usage { - users: Users { - total: Some(local_users), - active_halfyear: Some(local_active_halfyear), - active_month: Some(local_active_month), - }, - local_posts: Some(local_posts), - local_comments: None, - }, - metadata, - }) -} - -pub async fn nodeinfo_2_1() -> Result { - const NODEINFO_2_1_CACHE_KEY: &str = "nodeinfo_2_1"; - - let cached = cache::get::(NODEINFO_2_1_CACHE_KEY)?; - - if let Some(nodeinfo) = cached { - Ok(nodeinfo) - } else { - let nodeinfo = get_new_nodeinfo_2_1().await?; - cache::set(NODEINFO_2_1_CACHE_KEY, &nodeinfo, 60 * 60)?; - Ok(nodeinfo) - } -} - -pub async fn nodeinfo_2_0() -> Result { - Ok(nodeinfo_2_1().await?.into()) -} - -#[crate::export(js_name = "nodeinfo_2_1")] -pub async fn nodeinfo_2_1_as_json() -> Result { - Ok(serde_json::to_value(nodeinfo_2_1().await?)?) -} - -#[crate::export(js_name = "nodeinfo_2_0")] -pub async fn nodeinfo_2_0_as_json() -> Result { - Ok(serde_json::to_value(nodeinfo_2_0().await?)?) -}