forked from jmug/stoatchat
Compare commits
3
Commits
20230303-2
...
20230304-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31b9f18921 | ||
|
|
fd80823910 | ||
|
|
6bd8221eda |
Generated
+3
-3
@@ -2792,7 +2792,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.5.13"
|
version = "0.5.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-std",
|
"async-std",
|
||||||
"async-tungstenite",
|
"async-tungstenite",
|
||||||
@@ -2808,7 +2808,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.5.13"
|
version = "0.5.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-std",
|
"async-std",
|
||||||
@@ -2848,7 +2848,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.5.13"
|
version = "0.5.14"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-bonfire"
|
name = "revolt-bonfire"
|
||||||
version = "0.5.13"
|
version = "0.5.14"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-delta"
|
name = "revolt-delta"
|
||||||
version = "0.5.13"
|
version = "0.5.14"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
authors = ["Paul Makles <paulmakles@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use revolt_quark::{
|
use revolt_quark::{
|
||||||
models::message::{PartialMessage, SendableEmbed},
|
models::message::{PartialMessage, SendableEmbed},
|
||||||
models::{Message, User},
|
models::{Message, User},
|
||||||
|
perms,
|
||||||
types::january::Embed,
|
types::january::Embed,
|
||||||
Db, Error, Ref, Result, Timestamp,
|
Db, Error, Permission, Ref, Result, Timestamp,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rocket::serde::json::Json;
|
use rocket::serde::json::Json;
|
||||||
@@ -28,7 +29,7 @@ pub struct DataEditMessage {
|
|||||||
pub async fn req(
|
pub async fn req(
|
||||||
db: &Db,
|
db: &Db,
|
||||||
user: User,
|
user: User,
|
||||||
target: String,
|
target: Ref,
|
||||||
msg: Ref,
|
msg: Ref,
|
||||||
edit: Json<DataEditMessage>,
|
edit: Json<DataEditMessage>,
|
||||||
) -> Result<Json<Message>> {
|
) -> Result<Json<Message>> {
|
||||||
@@ -36,10 +37,17 @@ pub async fn req(
|
|||||||
edit.validate()
|
edit.validate()
|
||||||
.map_err(|error| Error::FailedValidation { error })?;
|
.map_err(|error| Error::FailedValidation { error })?;
|
||||||
|
|
||||||
|
// Ensure we have permissions to send a message
|
||||||
|
let channel = target.as_channel(db).await?;
|
||||||
|
let mut permissions = perms(&user).channel(&channel);
|
||||||
|
permissions
|
||||||
|
.throw_permission_and_view_channel(db, Permission::SendMessage)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Message::validate_sum(&edit.content, &edit.embeds)?;
|
Message::validate_sum(&edit.content, &edit.embeds)?;
|
||||||
|
|
||||||
let mut message = msg.as_message(db).await?;
|
let mut message = msg.as_message(db).await?;
|
||||||
if message.channel != target {
|
if message.channel != channel.id() {
|
||||||
return Err(Error::NotFound);
|
return Err(Error::NotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ mod push;
|
|||||||
mod root;
|
mod root;
|
||||||
mod safety;
|
mod safety;
|
||||||
mod servers;
|
mod servers;
|
||||||
|
mod stats;
|
||||||
mod sync;
|
mod sync;
|
||||||
mod users;
|
mod users;
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ pub fn mount(mut rocket: Rocket<Build>) -> Rocket<Build> {
|
|||||||
mount_endpoints_and_merged_docs! {
|
mount_endpoints_and_merged_docs! {
|
||||||
rocket, "/".to_owned(), settings,
|
rocket, "/".to_owned(), settings,
|
||||||
"/" => (vec![], custom_openapi_spec()),
|
"/" => (vec![], custom_openapi_spec()),
|
||||||
"" => openapi_get_routes_spec![root::root, root::ping],
|
"" => openapi_get_routes_spec![root::root, stats::stats, root::ping],
|
||||||
"/users" => users::routes(),
|
"/users" => users::routes(),
|
||||||
"/bots" => bots::routes(),
|
"/bots" => bots::routes(),
|
||||||
"/channels" => channels::routes(),
|
"/channels" => channels::routes(),
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
use revolt_quark::models::stats::Stats;
|
||||||
|
use revolt_quark::{Db, Result};
|
||||||
|
|
||||||
|
use rocket::serde::json::Json;
|
||||||
|
|
||||||
|
/// # Query Stats
|
||||||
|
///
|
||||||
|
/// Fetch various technical statistics.
|
||||||
|
#[openapi(tag = "Core")]
|
||||||
|
#[get("/stats")]
|
||||||
|
pub async fn stats(db: &Db) -> Result<Json<Stats>> {
|
||||||
|
Ok(Json(db.generate_stats().await?))
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "revolt-quark"
|
name = "revolt-quark"
|
||||||
version = "0.5.13"
|
version = "0.5.14"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
use crate::{models::stats::Stats, AbstractStats, Result};
|
||||||
|
|
||||||
|
use super::super::DummyDb;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractStats for DummyDb {
|
||||||
|
async fn generate_stats(&self) -> Result<Stats> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ use crate::AbstractDatabase;
|
|||||||
|
|
||||||
pub mod admin {
|
pub mod admin {
|
||||||
pub mod migrations;
|
pub mod migrations;
|
||||||
|
pub mod stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod media {
|
pub mod media {
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use bson::{from_document, Document};
|
||||||
|
use futures::StreamExt;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
models::stats::{Index, Stats},
|
||||||
|
AbstractStats, Error, Result,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::super::MongoDb;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl AbstractStats for MongoDb {
|
||||||
|
async fn generate_stats(&self) -> Result<Stats> {
|
||||||
|
let mut indices = HashMap::new();
|
||||||
|
let mut coll_stats = HashMap::new();
|
||||||
|
|
||||||
|
let collection_names =
|
||||||
|
self.db()
|
||||||
|
.list_collection_names(None)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "list_collection_names",
|
||||||
|
with: "database",
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for collection in collection_names {
|
||||||
|
indices.insert(
|
||||||
|
collection.to_string(),
|
||||||
|
self.col::<Document>(&collection)
|
||||||
|
.aggregate(
|
||||||
|
vec![doc! {
|
||||||
|
"$indexStats": { }
|
||||||
|
}],
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "aggregate",
|
||||||
|
with: "col",
|
||||||
|
})?
|
||||||
|
.filter_map(|s| async { s.ok() })
|
||||||
|
.collect::<Vec<Document>>()
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|doc| from_document(doc).ok())
|
||||||
|
.collect::<Vec<Index>>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
coll_stats.insert(
|
||||||
|
collection.to_string(),
|
||||||
|
self.col::<Document>(&collection)
|
||||||
|
.aggregate(
|
||||||
|
vec![doc! {
|
||||||
|
"$collStats": {
|
||||||
|
"latencyStats": {
|
||||||
|
"histograms": true
|
||||||
|
},
|
||||||
|
"storageStats": {},
|
||||||
|
"count": {},
|
||||||
|
"queryExecStats": {}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|_| Error::DatabaseError {
|
||||||
|
operation: "aggregate",
|
||||||
|
with: "col",
|
||||||
|
})?
|
||||||
|
.filter_map(|s| async { s.ok() })
|
||||||
|
.collect::<Vec<Document>>()
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|doc| from_document(doc).ok())
|
||||||
|
.next()
|
||||||
|
.ok_or(Error::DatabaseError {
|
||||||
|
operation: "next aggregation",
|
||||||
|
with: "col",
|
||||||
|
})?,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Stats {
|
||||||
|
indices,
|
||||||
|
coll_stats,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ use crate::{util::manipulation::prefix_keys, AbstractDatabase, Error, Result};
|
|||||||
|
|
||||||
pub mod admin {
|
pub mod admin {
|
||||||
pub mod migrations;
|
pub mod migrations;
|
||||||
|
pub mod stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod media {
|
pub mod media {
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use iso8601_timestamp::Timestamp;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Index access information
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
pub struct IndexAccess {
|
||||||
|
/// Operations since timestamp
|
||||||
|
ops: i32,
|
||||||
|
|
||||||
|
/// Timestamp at which data keeping begun
|
||||||
|
since: Timestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection index
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
pub struct Index {
|
||||||
|
/// Index name
|
||||||
|
name: String,
|
||||||
|
|
||||||
|
/// Access information
|
||||||
|
accesses: IndexAccess,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Histogram entry
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
pub struct LatencyHistogramEntry {
|
||||||
|
/// Time
|
||||||
|
micros: i64,
|
||||||
|
|
||||||
|
/// Count
|
||||||
|
count: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection latency stats
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
pub struct LatencyStats {
|
||||||
|
/// Total operations
|
||||||
|
ops: i64,
|
||||||
|
|
||||||
|
/// Timestamp at which data keeping begun
|
||||||
|
latency: i64,
|
||||||
|
|
||||||
|
/// Histogram representation of latency data
|
||||||
|
histogram: Vec<LatencyHistogramEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection storage stats
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct StorageStats {
|
||||||
|
/// Uncompressed data size
|
||||||
|
size: i64,
|
||||||
|
|
||||||
|
/// Data size on disk
|
||||||
|
storage_size: i64,
|
||||||
|
|
||||||
|
/// Total size of all indexes
|
||||||
|
total_index_size: i64,
|
||||||
|
|
||||||
|
/// Sum of storage size and total index size
|
||||||
|
total_size: i64,
|
||||||
|
|
||||||
|
/// Individual index sizes
|
||||||
|
index_sizes: HashMap<String, i64>,
|
||||||
|
|
||||||
|
/// Number of documents in collection
|
||||||
|
count: i64,
|
||||||
|
|
||||||
|
/// Average size of each document
|
||||||
|
avg_obj_size: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Query collection scan stats
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CollectionScans {
|
||||||
|
/// Number of total collection scans
|
||||||
|
total: i64,
|
||||||
|
|
||||||
|
/// Number of total collection scans not using a tailable cursor
|
||||||
|
non_tailable: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection query execution stats
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct QueryExecStats {
|
||||||
|
/// Stats regarding collection scans
|
||||||
|
collection_scans: CollectionScans,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collection stats
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct CollectionStats {
|
||||||
|
/// Namespace
|
||||||
|
ns: String,
|
||||||
|
|
||||||
|
/// Local time
|
||||||
|
local_time: Timestamp,
|
||||||
|
|
||||||
|
/// Latency stats
|
||||||
|
latency_stats: HashMap<String, LatencyStats>,
|
||||||
|
|
||||||
|
/// Query exec stats
|
||||||
|
query_exec_stats: QueryExecStats,
|
||||||
|
|
||||||
|
/// Number of documents in collection
|
||||||
|
count: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Server Stats
|
||||||
|
#[derive(Serialize, JsonSchema, Debug)]
|
||||||
|
pub struct Stats {
|
||||||
|
/// Index usage information
|
||||||
|
pub indices: HashMap<String, Vec<Index>>,
|
||||||
|
|
||||||
|
/// Collection stats
|
||||||
|
pub coll_stats: HashMap<String, CollectionStats>,
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
mod admin {
|
mod admin {
|
||||||
pub mod migrations;
|
pub mod migrations;
|
||||||
pub mod simple;
|
pub mod simple;
|
||||||
|
pub mod stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod media {
|
mod media {
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
use crate::{models::stats::Stats, Result};
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AbstractStats: Sync + Send {
|
||||||
|
async fn generate_stats(&self) -> Result<Stats>;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
mod admin {
|
mod admin {
|
||||||
pub mod migrations;
|
pub mod migrations;
|
||||||
|
pub mod stats;
|
||||||
}
|
}
|
||||||
|
|
||||||
mod media {
|
mod media {
|
||||||
@@ -32,6 +33,7 @@ mod safety {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use admin::migrations::AbstractMigrations;
|
pub use admin::migrations::AbstractMigrations;
|
||||||
|
pub use admin::stats::AbstractStats;
|
||||||
|
|
||||||
pub use media::attachment::AbstractAttachment;
|
pub use media::attachment::AbstractAttachment;
|
||||||
pub use media::emoji::AbstractEmoji;
|
pub use media::emoji::AbstractEmoji;
|
||||||
@@ -56,6 +58,7 @@ pub trait AbstractDatabase:
|
|||||||
Sync
|
Sync
|
||||||
+ Send
|
+ Send
|
||||||
+ AbstractMigrations
|
+ AbstractMigrations
|
||||||
|
+ AbstractStats
|
||||||
+ AbstractAttachment
|
+ AbstractAttachment
|
||||||
+ AbstractEmoji
|
+ AbstractEmoji
|
||||||
+ AbstractChannel
|
+ AbstractChannel
|
||||||
|
|||||||
Reference in New Issue
Block a user