diff --git a/Cargo.lock b/Cargo.lock index ef42db7..6cba0f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,12 @@ version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "askama" version = "0.12.1" @@ -513,6 +519,7 @@ dependencies = [ "metrics", "metrics-exporter-prometheus", "metrics-process", + "num-format", "rocksdb", "serde", "serde_with", @@ -1395,6 +1402,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + [[package]] name = "num-traits" version = "0.2.19" diff --git a/constellation/Cargo.toml b/constellation/Cargo.toml index ef78a02..9533935 100644 --- a/constellation/Cargo.toml +++ b/constellation/Cargo.toml @@ -20,6 +20,7 @@ mediatype = "0.19.18" metrics = "0.24.1" metrics-exporter-prometheus = { version = "0.16.1", default-features = false, features = ["http-listener"] } metrics-process = "2.4.0" +num-format = "0.4.4" rocksdb = { version = "0.23.0", optional = true } serde = { version = "1.0.215", features = ["derive"] } serde_with = { version = "3.12.0", features = ["hex"] } diff --git a/constellation/src/server/filters.rs b/constellation/src/server/filters.rs index d406de0..9339a50 100644 --- a/constellation/src/server/filters.rs +++ b/constellation/src/server/filters.rs @@ -1,4 +1,5 @@ use links::{parse_any_link, Link}; +use num_format::{Locale, ToFormattedString}; pub fn to_browseable(s: &str) -> askama::Result> { Ok({ @@ -17,3 +18,7 @@ pub fn to_browseable(s: &str) -> askama::Result> { } }) } + +pub fn human_number(n: &u64) -> askama::Result { + Ok(n.to_formatted_string(&Locale::en)) +} diff --git a/constellation/src/server/mod.rs b/constellation/src/server/mod.rs index ec4f9f3..bb344a7 100644 --- a/constellation/src/server/mod.rs +++ b/constellation/src/server/mod.rs @@ -4,11 +4,12 @@ use bincode::Options; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use std::collections::HashMap; +use std::time::{UNIX_EPOCH, Duration}; use tokio::net::{TcpListener, ToSocketAddrs}; use tokio::task::block_in_place; use tokio_util::sync::CancellationToken; -use crate::storage::LinkReader; +use crate::storage::{LinkReader, StorageStats}; use constellation::{CountsByCount, Did, RecordId}; mod acceptable; @@ -19,13 +20,22 @@ use acceptable::{acceptable, ExtractAccept}; const DEFAULT_CURSOR_LIMIT: u64 = 16; const DEFAULT_CURSOR_LIMIT_MAX: u64 = 100; +const INDEX_BEGAN_AT_TS: u64 = 1738083600; // TODO: not this + + pub async fn serve(store: S, addr: A, stay_alive: CancellationToken) -> anyhow::Result<()> where S: LinkReader, A: ToSocketAddrs, { let app = Router::new() - .route("/", get(hello)) + .route( + "/", + get({ + let store = store.clone(); + move |accept| async { block_in_place(|| hello(accept, store)) } + }), + ) .route( "/links/count", get({ @@ -93,11 +103,22 @@ where #[template(path = "hello.html.j2")] struct HelloReponse { help: &'static str, + days_indexed: u64, + stats: StorageStats, } -async fn hello(accept: ExtractAccept) -> impl IntoResponse { - acceptable(accept, HelloReponse { - help: "open this URL in a web browser (or request with Accept: text/html) for information about this API." - }) +fn hello(accept: ExtractAccept, store: impl LinkReader) -> Result { + let stats = store + .get_stats() + .map_err(|_| http::StatusCode::INTERNAL_SERVER_ERROR)?; + let days_indexed = (UNIX_EPOCH + Duration::from_secs(INDEX_BEGAN_AT_TS)) + .elapsed() + .map_err(|_| http::StatusCode::INTERNAL_SERVER_ERROR)? + .as_secs() / 86400; + Ok(acceptable(accept, HelloReponse { + help: "open this URL in a web browser (or request with Accept: text/html) for information about this API.", + days_indexed, + stats, + })) } #[derive(Clone, Deserialize)] diff --git a/constellation/src/storage/mod.rs b/constellation/src/storage/mod.rs index d960653..69f8283 100644 --- a/constellation/src/storage/mod.rs +++ b/constellation/src/storage/mod.rs @@ -1,6 +1,7 @@ use anyhow::Result; use constellation::{ActionableEvent, CountsByCount, Did, RecordId}; use std::collections::HashMap; +use serde::{Deserialize, Serialize}; pub mod mem_store; pub use mem_store::MemStorage; @@ -17,7 +18,7 @@ pub struct PagedAppendingCollection { pub next: Option, } -#[derive(Debug, PartialEq)] +#[derive(Debug, Deserialize, Serialize, PartialEq)] pub struct StorageStats { /// estimate of how many accounts we've seen create links. the _subjects_ of any links are not represented here. /// for example: new user A follows users B and C. this count will only increment by one, for A. diff --git a/constellation/templates/base.html.j2 b/constellation/templates/base.html.j2 index 3c8380e..21e2065 100644 --- a/constellation/templates/base.html.j2 +++ b/constellation/templates/base.html.j2 @@ -39,6 +39,11 @@ padding: 0.5em 0.3em; max-width: 100%; } + .stat { + color: #f90; + font-size: 1.618rem; + font-weight: bold; + } details { margin: 2em 0 3em; } @@ -49,7 +54,7 @@ -

This is a constellation 🌌 server from microcosm ✨

+

This is a constellation 🌌 API server from microcosm ✨

{% block content %}{% endblock %}