diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 7db6435..90c348c 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -1038,6 +1038,19 @@ components: registrations: description: Whether registrations are enabled. type: boolean + stats: + description: Statistics about how much information the instance contains. + type: object + properties: + user_count: + description: Users registered on this instance + type: integer + status_count: + description: Statuses authored by users on instance. + type: integer + domain_count: + description: Domains federated with this instance. + type: integer login_message: description: Login message for signer. type: string diff --git a/src/mastodon_api/instance/types.rs b/src/mastodon_api/instance/types.rs index ecd83f9..0fe30dd 100644 --- a/src/mastodon_api/instance/types.rs +++ b/src/mastodon_api/instance/types.rs @@ -5,6 +5,13 @@ use crate::config::{BlockchainConfig, Config}; use crate::ethereum::contracts::ContractSet; use crate::mastodon_api::MASTODON_API_VERSION; +#[derive(Serialize)] +struct InstanceStats { + user_count: i64, + status_count: i64, + domain_count: i64, +} + #[derive(Serialize)] struct BlockchainFeatures { minter: bool, @@ -27,6 +34,7 @@ pub struct InstanceInfo { description: String, version: String, registrations: bool, + stats: InstanceStats, login_message: String, post_character_limit: usize, @@ -43,7 +51,13 @@ fn get_full_api_version(version: &str) -> String { } impl InstanceInfo { - pub fn create(config: &Config, maybe_blockchain: Option<&ContractSet>) -> Self { + pub fn create( + config: &Config, + maybe_blockchain: Option<&ContractSet>, + user_count: i64, + post_count: i64, + peer_count: i64, + ) -> Self { let mut blockchains = vec![]; match config.blockchain() { Some(BlockchainConfig::Ethereum(ethereum_config)) => { @@ -90,6 +104,11 @@ impl InstanceInfo { description: config.instance_description.clone(), version: get_full_api_version(&config.version), registrations: config.registrations_open, + stats: InstanceStats { + user_count, + status_count: post_count, + domain_count: peer_count, + }, login_message: config.login_message.clone(), post_character_limit: config.post_character_limit, blockchains: blockchains, diff --git a/src/mastodon_api/instance/views.rs b/src/mastodon_api/instance/views.rs index 22c597f..8a343b1 100644 --- a/src/mastodon_api/instance/views.rs +++ b/src/mastodon_api/instance/views.rs @@ -1,18 +1,32 @@ use actix_web::{get, web, HttpResponse, Scope}; use crate::config::Config; +use crate::database::{Pool, get_database_client}; use crate::errors::HttpError; use crate::ethereum::contracts::ContractSet; +use crate::models::{ + instances::queries::get_peer_count, + posts::queries::get_local_post_count, + users::queries::get_user_count, +}; use super::types::InstanceInfo; #[get("")] async fn instance_view( config: web::Data, + db_pool: web::Data, maybe_blockchain: web::Data>, ) -> Result { + let db_client = &**get_database_client(&db_pool).await?; + let user_count = get_user_count(db_client).await?; + let post_count = get_local_post_count(db_client).await?; + let peer_count = get_peer_count(db_client).await?; let instance = InstanceInfo::create( config.as_ref(), maybe_blockchain.as_ref().as_ref(), + user_count, + post_count, + peer_count, ); Ok(HttpResponse::Ok().json(instance)) } diff --git a/src/models/instances/mod.rs b/src/models/instances/mod.rs new file mode 100644 index 0000000..84c032e --- /dev/null +++ b/src/models/instances/mod.rs @@ -0,0 +1 @@ +pub mod queries; diff --git a/src/models/instances/queries.rs b/src/models/instances/queries.rs new file mode 100644 index 0000000..0ba5ef5 --- /dev/null +++ b/src/models/instances/queries.rs @@ -0,0 +1,28 @@ +use tokio_postgres::GenericClient; + +use crate::errors::DatabaseError; + +pub async fn create_instance( + db_client: &impl GenericClient, + hostname: &str, +) -> Result<(), DatabaseError> { + db_client.execute( + " + INSERT INTO instance VALUES ($1) + ON CONFLICT DO NOTHING + ", + &[&hostname], + ).await?; + Ok(()) +} + +pub async fn get_peer_count( + db_client: &impl GenericClient, +) -> Result { + let row = db_client.query_one( + "SELECT count(instance) FROM instance", + &[], + ).await?; + let count = row.try_get("count")?; + Ok(count) +} diff --git a/src/models/mod.rs b/src/models/mod.rs index 8ec7ed6..3cc757a 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,5 +1,6 @@ pub mod attachments; pub mod cleanup; +pub mod instances; pub mod invoices; pub mod markers; pub mod notifications; diff --git a/src/models/profiles/queries.rs b/src/models/profiles/queries.rs index 42b21af..1b098b0 100644 --- a/src/models/profiles/queries.rs +++ b/src/models/profiles/queries.rs @@ -10,6 +10,7 @@ use crate::models::cleanup::{ find_orphaned_ipfs_objects, DeletionQueue, }; +use crate::models::instances::queries::create_instance; use crate::models::relationships::types::RelationshipType; use crate::utils::currencies::Currency; use crate::utils::id::new_uuid; @@ -29,13 +30,7 @@ pub async fn create_profile( ) -> Result { let profile_id = new_uuid(); if let Some(ref hostname) = profile_data.hostname { - db_client.execute( - " - INSERT INTO instance VALUES ($1) - ON CONFLICT DO NOTHING - ", - &[&hostname], - ).await?; + create_instance(db_client, hostname).await?; }; let row = db_client.query_one( "