This repository has been archived on 2025-01-27. You can view files and clone it, but cannot push or open issues or pull requests.
puyoskey-firefish/packages/backend-rs/src/federation/nodeinfo/generate.rs
HidemaruOwO 41186813ed ♻️ Refer develop version firefish (Cargo.lock, Cargo.toml, locales/ca-ES.yml, package.json, packages/backend-rs/src/cache/redis.rs, packages/backend-rs/src/database/redis.rs, packages/backend-rs/src/federation/activitypub/object/follow.rs, packages/backend-rs/src/federation/activitypub/object/like.rs, packages/backend-rs/src/federation/activitypub/object/relay.rs, packages/backend-rs/src/federation/internal_actor/instance.rs, packages/backend-rs/src/federation/internal_actor/relay.rs, packages/backend-rs/src/federation/nodeinfo/fetch.rs, packages/backend-rs/src/federation/nodeinfo/generate.rs, packages/backend-rs/src/misc/convert_host.rs, packages/backend-rs/src/misc/emoji.rs, packages/backend-rs/src/misc/emoji/reaction.rs, packages/backend-rs/src/misc/get_image_size.rs, packages/backend-rs/src/misc/latest_version.rs, packages/backend-rs/src/misc/password.rs, packages/backend-rs/src/misc/random_icon.rs, packages/backend-rs/src/misc/should_nyaify.rs, packages/backend-rs/src/misc/translate.rs, packages/backend-rs/src/service/antenna/check_hit.rs, packages/backend-rs/src/service/antenna/process_new_note.rs, packages/backend-rs/src/service/push_notification.rs, packages/backend-rs/src/service/stream.rs, packages/backend-rs/src/util/error_chain.rs, packages/backend-rs/src/util/http_client.rs, packages/backend/assets/resource-unknown.svg, packages/backend/package.json, packages/client/package.json, packages/client/src/components/MkInstanceCardMini.vue, packages/client/src/components/MkInstanceTicker.vue, packages/client/src/components/MkUrlPreview.vue, packages/client/src/ui/_common_/statusbar-federation.vue, packages/client/src/widgets/federation.vue, packages/client/src/widgets/instance-cloud.vue, packages/firefish-js/package.json, packages/macro-rs/macros-impl/Cargo.toml, packages/macro-rs/macros-impl/src/lib.rs, packages/macro-rs/macros-impl/src/napi.rs, packages/macro-rs/macros/src/lib.rs, packages/sw/package.json, pnpm-lock.yaml)
2024-08-24 09:40:20 +09:00

163 lines
5.2 KiB
Rust

//! NodeInfo generator
use crate::{
cache::Cache,
config::{local_server_info, CONFIG},
database::db_conn,
federation::nodeinfo::schema::*,
misc,
model::entity::{note, user},
};
use chrono::Duration;
use sea_orm::prelude::*;
use serde_json::json;
use std::collections::HashMap;
static NODEINFO_CACHE: Cache<Nodeinfo21> = Cache::new_with_ttl(Duration::hours(1));
/// Fetches the number of total/active local users and local posts.
///
/// # Return value
/// A tuple containing the following information in this order:
/// * the total number of local users
/// * the total number of local users active in the last 6 months
/// * the total number of local users active in the last month (MAU)
/// * the total number of posts from local users
async fn statistics() -> Result<(u64, u64, u64, u64), DbErr> {
let db = db_conn().await?;
let now = chrono::Utc::now();
const MONTH: chrono::TimeDelta = chrono::Duration::days(30);
const HALF_YEAR: chrono::TimeDelta = chrono::Duration::days(183);
let local_users = misc::user::count::local_total(db);
// We don't need to care about the number of system actors here,
// because their last active date is null
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
)
}
/// Generates NodeInfo (version 2.1) of the local server.
/// This function doesn't use caches and returns the latest information.
async fn generate_nodeinfo_2_1() -> Result<Nodeinfo21, DbErr> {
tracing::info!("generating NodeInfo");
let (local_users, local_active_halfyear, local_active_month, local_posts) =
statistics().await?;
let meta = local_server_info().await?;
let mut metadata = HashMap::from([
(
"nodeName".to_owned(),
json!(meta.name.unwrap_or_else(|| CONFIG.host.clone())),
),
("nodeDescription".to_owned(), json!(meta.description)),
("repositoryUrl".to_owned(), json!(meta.repository_url)),
(
"enableLocalTimeline".to_owned(),
json!(!meta.disable_local_timeline),
),
(
"enableRecommendedTimeline".to_owned(),
json!(!meta.disable_recommended_timeline),
),
(
"enableGlobalTimeline".to_owned(),
json!(!meta.disable_global_timeline),
),
(
"enableGuestTimeline".to_owned(),
json!(meta.enable_guest_timeline),
),
(
"maintainer".to_owned(),
json!({"name":meta.maintainer_name,"email":meta.maintainer_email}),
),
("proxyAccountName".to_owned(), json!(meta.proxy_account_id)),
(
"themeColor".to_owned(),
json!(meta.theme_color.unwrap_or_else(|| "#31748f".to_owned())),
),
]);
metadata.shrink_to_fit();
Ok(Nodeinfo21 {
software: Software21 {
name: "firefish".to_owned(),
version: CONFIG.version.clone(),
repository: Some(meta.repository_url),
homepage: Some("https://firefish.dev/firefish/firefish".to_owned()),
},
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 as u32),
active_halfyear: Some(local_active_halfyear as u32),
active_month: Some(local_active_month as u32),
},
local_posts: Some(local_posts as u32),
local_comments: None,
},
metadata,
})
}
/// Returns NodeInfo (version 2.1) of the local server.
pub async fn nodeinfo_2_1() -> Result<Nodeinfo21, DbErr> {
if let Some(nodeinfo) = NODEINFO_CACHE.get() {
return Ok(nodeinfo);
}
let nodeinfo = generate_nodeinfo_2_1().await?;
tracing::info!("updating cache");
NODEINFO_CACHE.set(nodeinfo.clone());
Ok(nodeinfo)
}
/// Returns NodeInfo (version 2.0) of the local server.
pub async fn nodeinfo_2_0() -> Result<Nodeinfo20, DbErr> {
Ok(nodeinfo_2_1().await?.into())
}
#[macros::for_ts]
#[error_doc::errors]
pub enum Error {
#[doc = "Database error"]
#[error(transparent)]
Db(#[from] DbErr),
#[error("failed to serialize nodeinfo into JSON")]
Json(#[from] serde_json::Error),
}
#[macros::ts_export(js_name = "nodeinfo_2_1")]
pub async fn nodeinfo_2_1_as_json() -> Result<serde_json::Value, Error> {
Ok(serde_json::to_value(nodeinfo_2_1().await?)?)
}
#[macros::ts_export(js_name = "nodeinfo_2_0")]
pub async fn nodeinfo_2_0_as_json() -> Result<serde_json::Value, Error> {
Ok(serde_json::to_value(nodeinfo_2_0().await?)?)
}