Compare commits
5 Commits
8ba0f02b98
...
06e1474396
Author | SHA1 | Date | |
---|---|---|---|
06e1474396 | |||
|
adb9c728f4 | ||
|
fc02eaea4a | ||
|
91f87302fb | ||
|
97f186dc33 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -3,3 +3,5 @@
|
||||
/venv
|
||||
.cargo
|
||||
/.idea
|
||||
web/static/index.html
|
||||
web/static/assets
|
||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "reminder-dashboard"]
|
||||
path = reminder-dashboard
|
||||
url = gitea@gitea.jellypro.xyz:jude/reminder-dashboard
|
26
Cargo.lock
generated
26
Cargo.lock
generated
@ -2059,6 +2059,27 @@ dependencies = [
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prometheus"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fnv",
|
||||
"lazy_static",
|
||||
"memchr",
|
||||
"parking_lot",
|
||||
"protobuf",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "protobuf"
|
||||
version = "2.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
@ -2173,7 +2194,7 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "reminder-rs"
|
||||
version = "1.6.47"
|
||||
version = "1.6.48"
|
||||
dependencies = [
|
||||
"base64 0.21.4",
|
||||
"chrono",
|
||||
@ -2201,7 +2222,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reminder_web"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"chrono",
|
||||
@ -2210,6 +2231,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"oauth2",
|
||||
"prometheus",
|
||||
"rand",
|
||||
"reqwest",
|
||||
"rocket",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "reminder-rs"
|
||||
version = "1.6.47"
|
||||
version = "1.6.48"
|
||||
authors = ["Jude Southworth <judesouthworth@pm.me>"]
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0 only"
|
||||
@ -43,6 +43,7 @@ assets = [
|
||||
["conf/default.env", "etc/reminder-rs/config.env", "600"],
|
||||
["conf/Rocket.toml", "etc/reminder-rs/Rocket.toml", "600"],
|
||||
["web/static/**/*", "lib/reminder-rs/static", "644"],
|
||||
["reminder-dashboard/dist/static/**/*", "lib/reminder-rs/static", "644"],
|
||||
["web/templates/**/*", "lib/reminder-rs/templates", "644"],
|
||||
["healthcheck", "lib/reminder-rs/healthcheck", "755"],
|
||||
["cron.d/reminder_health", "etc/cron.d/reminder_health", "644"],
|
||||
|
1
reminder-dashboard
Submodule
1
reminder-dashboard
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 8ba7a39ce532907b845c54dd3b54bf2a4c7a04e9
|
@ -114,6 +114,8 @@ pub async fn offset(
|
||||
#[description = "Number of minutes to offset by"] minutes: Option<isize>,
|
||||
#[description = "Number of seconds to offset by"] seconds: Option<isize>,
|
||||
) -> Result<(), Error> {
|
||||
ctx.defer().await?;
|
||||
|
||||
let combined_time = hours.map_or(0, |h| h * HOUR as isize)
|
||||
+ minutes.map_or(0, |m| m * MINUTE as isize)
|
||||
+ seconds.map_or(0, |s| s);
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "reminder_web"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
authors = ["jellywx <judesouthworth@pm.me>"]
|
||||
edition = "2018"
|
||||
|
||||
@ -19,3 +19,4 @@ lazy_static = "1.4.0"
|
||||
rand = "0.8"
|
||||
base64 = "0.13"
|
||||
csv = "1.2"
|
||||
prometheus = "0.13.3"
|
||||
|
@ -6,6 +6,7 @@ mod consts;
|
||||
mod macros;
|
||||
mod catchers;
|
||||
mod guards;
|
||||
mod metrics;
|
||||
mod routes;
|
||||
|
||||
use std::{env, path::Path};
|
||||
@ -25,7 +26,10 @@ use serenity::{
|
||||
};
|
||||
use sqlx::{MySql, Pool};
|
||||
|
||||
use crate::consts::{CNC_GUILD, DISCORD_OAUTH_AUTHORIZE, DISCORD_OAUTH_TOKEN, SUBSCRIPTION_ROLES};
|
||||
use crate::{
|
||||
consts::{CNC_GUILD, DISCORD_OAUTH_AUTHORIZE, DISCORD_OAUTH_TOKEN, SUBSCRIPTION_ROLES},
|
||||
metrics::{init_metrics, MetricProducer},
|
||||
};
|
||||
|
||||
type Database = MySql;
|
||||
|
||||
@ -64,7 +68,10 @@ pub async fn initialize(
|
||||
let static_path =
|
||||
if Path::new("web/static").exists() { "web/static" } else { "/lib/reminder-rs/static" };
|
||||
|
||||
init_metrics();
|
||||
|
||||
rocket::build()
|
||||
.attach(MetricProducer)
|
||||
.attach(Template::fairing())
|
||||
.register(
|
||||
"/",
|
||||
@ -85,12 +92,13 @@ pub async fn initialize(
|
||||
.mount(
|
||||
"/",
|
||||
routes![
|
||||
routes::index,
|
||||
routes::cookies,
|
||||
routes::index,
|
||||
routes::metrics::metrics,
|
||||
routes::privacy,
|
||||
routes::terms,
|
||||
routes::return_to_same_site,
|
||||
routes::report::report_error,
|
||||
routes::return_to_same_site,
|
||||
routes::terms,
|
||||
],
|
||||
)
|
||||
.mount(
|
||||
|
43
web/src/metrics.rs
Normal file
43
web/src/metrics.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use lazy_static::lazy_static;
|
||||
use prometheus::{IntCounterVec, Opts, Registry};
|
||||
use rocket::{
|
||||
fairing::{Fairing, Info, Kind},
|
||||
Data, Request, Response,
|
||||
};
|
||||
|
||||
lazy_static! {
|
||||
pub static ref REGISTRY: Registry = Registry::new();
|
||||
static ref REQUEST_COUNTER: IntCounterVec =
|
||||
IntCounterVec::new(Opts::new("requests", "Requests"), &["method", "route"]).unwrap();
|
||||
static ref RESPONSE_COUNTER: IntCounterVec =
|
||||
IntCounterVec::new(Opts::new("responses", "Responses"), &["status", "route"]).unwrap();
|
||||
}
|
||||
|
||||
pub fn init_metrics() {
|
||||
REGISTRY.register(Box::new(REQUEST_COUNTER.clone())).unwrap();
|
||||
}
|
||||
|
||||
pub struct MetricProducer;
|
||||
|
||||
#[rocket::async_trait]
|
||||
impl Fairing for MetricProducer {
|
||||
fn info(&self) -> Info {
|
||||
Info { name: "Metrics fairing", kind: Kind::Request }
|
||||
}
|
||||
|
||||
async fn on_request(&self, req: &mut Request<'_>, _data: &mut Data<'_>) {
|
||||
if let Some(route) = req.route() {
|
||||
REQUEST_COUNTER
|
||||
.with_label_values(&[req.method().as_str(), &route.uri.to_string()])
|
||||
.inc();
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_response<'r>(&self, req: &'r Request<'_>, resp: &mut Response<'r>) {
|
||||
if let Some(route) = req.route() {
|
||||
RESPONSE_COUNTER
|
||||
.with_label_values(&[&resp.status().code.to_string(), &route.uri.to_string()])
|
||||
.inc();
|
||||
}
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
|
@ -1,9 +1,13 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
use chrono::{naive::NaiveDateTime, Utc};
|
||||
use rand::{rngs::OsRng, seq::IteratorRandom};
|
||||
use rocket::{http::CookieJar, response::Redirect, serde::json::json};
|
||||
use rocket_dyn_templates::Template;
|
||||
use rocket::{
|
||||
fs::{relative, NamedFile},
|
||||
http::CookieJar,
|
||||
response::Redirect,
|
||||
serde::json::json,
|
||||
};
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use serenity::{
|
||||
client::Context,
|
||||
@ -27,7 +31,6 @@ use crate::{
|
||||
|
||||
pub mod api;
|
||||
pub mod export;
|
||||
pub mod guild;
|
||||
|
||||
type Unset<T> = Option<T>;
|
||||
|
||||
@ -657,22 +660,26 @@ async fn create_database_channel(
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
pub async fn dashboard_home(cookies: &CookieJar<'_>) -> Result<Template, Redirect> {
|
||||
pub async fn dashboard_home(cookies: &CookieJar<'_>) -> Result<NamedFile, Redirect> {
|
||||
if cookies.get_private("userid").is_some() {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("version", env!("CARGO_PKG_VERSION"));
|
||||
Ok(Template::render("dashboard", &map))
|
||||
NamedFile::open(Path::new(relative!("static/index.html"))).await.map_err(|e| {
|
||||
warn!("Couldn't render dashboard: {:?}", e);
|
||||
|
||||
Redirect::to("/login/discord")
|
||||
})
|
||||
} else {
|
||||
Err(Redirect::to("/login/discord"))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/<_..>")]
|
||||
pub async fn dashboard(cookies: &CookieJar<'_>) -> Result<Template, Redirect> {
|
||||
pub async fn dashboard(cookies: &CookieJar<'_>) -> Result<NamedFile, Redirect> {
|
||||
if cookies.get_private("userid").is_some() {
|
||||
let mut map = HashMap::new();
|
||||
map.insert("version", env!("CARGO_PKG_VERSION"));
|
||||
Ok(Template::render("dashboard", &map))
|
||||
NamedFile::open(Path::new(relative!("static/index.html"))).await.map_err(|e| {
|
||||
warn!("Couldn't render dashboard: {:?}", e);
|
||||
|
||||
Redirect::to("/login/discord")
|
||||
})
|
||||
} else {
|
||||
Err(Redirect::to("/login/discord"))
|
||||
}
|
||||
|
18
web/src/routes/metrics.rs
Normal file
18
web/src/routes/metrics.rs
Normal file
@ -0,0 +1,18 @@
|
||||
use prometheus;
|
||||
|
||||
use crate::metrics::REGISTRY;
|
||||
|
||||
#[get("/metrics")]
|
||||
pub async fn metrics() -> String {
|
||||
let encoder = prometheus::TextEncoder::new();
|
||||
let res_custom = encoder.encode_to_string(®ISTRY.gather());
|
||||
|
||||
match res_custom {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
warn!("Error encoding metrics: {:?}", e);
|
||||
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
pub mod admin;
|
||||
pub mod dashboard;
|
||||
pub mod login;
|
||||
pub mod metrics;
|
||||
pub mod report;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
@ -218,17 +218,16 @@ div.inset-content {
|
||||
margin-right: 10%;
|
||||
}
|
||||
|
||||
div.flash-message {
|
||||
div.flash-container {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
div.flash-message {
|
||||
width: calc(100% - 32px);
|
||||
margin: 16px !important;
|
||||
z-index: 99;
|
||||
bottom: 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.flash-message.is-active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
@ -633,6 +632,10 @@ li.highlight {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.button-row-edit > button {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.button-row .button-row-reminder {
|
||||
flex-grow: 0;
|
||||
padding: 2px;
|
||||
|
Loading…
Reference in New Issue
Block a user