Move postman and web inside src

This commit is contained in:
claude Bot committed 2024-03-24 20:23:16 +00:00
1 parent 075fde71df
commit 4a80d42f86
152 files changed
+807 -753

No files matched your search

Generated
+572 -324
View File
File diff suppressed because it is too large. Load diff
+8 -7
View File
@@ -10,7 +10,7 @@ description = "Reminder Bot for Discord, now in Rust"
poise = "0.6.1" poise = "0.6.1"
dotenv = "0.15" dotenv = "0.15"
tokio = { version = "1", features = ["process", "full"] } tokio = { version = "1", features = ["process", "full"] }
reqwest = "0.11" reqwest = { version = "0.12", features = ["json"] }
lazy-regex = "3.1" lazy-regex = "3.1"
regex = "1.10" regex = "1.10"
log = "0.4" log = "0.4"
@@ -29,12 +29,13 @@ sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "macros", "mysql",
base64 = "0.21" base64 = "0.21"
secrecy = "0.8.0" secrecy = "0.8.0"
futures = "0.3.30" futures = "0.3.30"
prometheus = "0.13.3"
[dependencies.postman] rocket = { version = "0.5.0", features = ["tls", "secrets", "json"] }
path = "postman" rocket_dyn_templates = { version = "0.1.0", features = ["tera"] }
serenity = { version = "0.12", default-features = false, features = ["builder", "cache", "client", "gateway", "http", "model", "utils", "rustls_backend"] }
[dependencies.reminder_web] oauth2 = "4"
path = "web" csv = "1.2"
axum = "0.7"
[dependencies.extract_derive] [dependencies.extract_derive]
path = "extract_derive" path = "extract_derive"
+11 -11
View File
@@ -1,28 +1,28 @@
[default] [default]
address = "0.0.0.0" address = "0.0.0.0"
port = 18920 port = 18920
template_dir = "web/templates" template_dir = "templates"
limits = { json = "10MiB" } limits = { json = "10MiB" }
[debug] [debug]
secret_key = "tR8krio5FXTnnyIZNiJDXPondz0kI1v6X6BXZcBGIRY=" secret_key = "tR8krio5FXTnnyIZNiJDXPondz0kI1v6X6BXZcBGIRY="
[debug.tls] [debug.tls]
certs = "web/private/rsa_sha256_cert.pem" certs = "private/rsa_sha256_cert.pem"
key = "web/private/rsa_sha256_key.pem" key = "private/rsa_sha256_key.pem"
[debug.rsa_sha256.tls] [debug.rsa_sha256.tls]
certs = "web/private/rsa_sha256_cert.pem" certs = "private/rsa_sha256_cert.pem"
key = "web/private/rsa_sha256_key.pem" key = "private/rsa_sha256_key.pem"
[debug.ecdsa_nistp256_sha256.tls] [debug.ecdsa_nistp256_sha256.tls]
certs = "web/private/ecdsa_nistp256_sha256_cert.pem" certs = "private/ecdsa_nistp256_sha256_cert.pem"
key = "web/private/ecdsa_nistp256_sha256_key_pkcs8.pem" key = "private/ecdsa_nistp256_sha256_key_pkcs8.pem"
[debug.ecdsa_nistp384_sha384.tls] [debug.ecdsa_nistp384_sha384.tls]
certs = "web/private/ecdsa_nistp384_sha384_cert.pem" certs = "private/ecdsa_nistp384_sha384_cert.pem"
key = "web/private/ecdsa_nistp384_sha384_key_pkcs8.pem" key = "private/ecdsa_nistp384_sha384_key_pkcs8.pem"
[debug.ed25519.tls] [debug.ed25519.tls]
certs = "web/private/ed25519_cert.pem" certs = "private/ed25519_cert.pem"
key = "eb/private/ed25519_key.pem" key = "private/ed25519_key.pem"
-16
View File
@@ -1,16 +0,0 @@
[package]
name = "postman"
version = "0.1.0"
edition = "2021"
[dependencies]
tokio = { version = "1", features = ["process", "full"] }
regex = "1.9"
log = "0.4"
chrono = "0.4"
chrono-tz = { version = "0.8", features = ["serde"] }
lazy_static = "1.4"
num-integer = "0.1"
serde = "1.0"
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal", "chrono", "json"]}
serenity = { version = "0.12", default-features = false, features = ["builder", "cache", "client", "gateway", "http", "model", "utils", "rustls_backend"] }
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+3 -3
View File
@@ -1,4 +1,4 @@
use chrono::NaiveDateTime; use chrono::DateTime;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@@ -24,10 +24,10 @@ impl Recordable for Options {
let parsed = natural_parser(&until, &timezone.to_string()).await; let parsed = natural_parser(&until, &timezone.to_string()).await;
if let Some(timestamp) = parsed { if let Some(timestamp) = parsed {
match NaiveDateTime::from_timestamp_opt(timestamp, 0) { match DateTime::from_timestamp(timestamp, 0) {
Some(dt) => { Some(dt) => {
channel.paused = true; channel.paused = true;
channel.paused_until = Some(dt); channel.paused_until = Some(dt.naive_utc());
channel.commit_changes(&ctx.data().database).await; channel.commit_changes(&ctx.data().database).await;
+11 -3
View File
@@ -12,12 +12,15 @@ mod event_handlers;
#[cfg(not(test))] #[cfg(not(test))]
mod hooks; mod hooks;
mod interval_parser; mod interval_parser;
mod metrics;
#[cfg(not(test))] #[cfg(not(test))]
mod models; mod models;
mod postman;
#[cfg(test)] #[cfg(test)]
mod test; mod test;
mod time_parser; mod time_parser;
mod utils; mod utils;
mod web;
use std::{ use std::{
collections::HashMap, collections::HashMap,
@@ -28,7 +31,7 @@ use std::{
}; };
use chrono_tz::Tz; use chrono_tz::Tz;
use log::{error, warn}; use log::warn;
use poise::serenity_prelude::{ use poise::serenity_prelude::{
model::{ model::{
gateway::GatewayIntents, gateway::GatewayIntents,
@@ -39,6 +42,7 @@ use poise::serenity_prelude::{
use sqlx::{MySql, Pool}; use sqlx::{MySql, Pool};
use tokio::sync::{broadcast, broadcast::Sender, RwLock}; use tokio::sync::{broadcast, broadcast::Sender, RwLock};
use crate::metrics::init_metrics;
#[cfg(test)] #[cfg(test)]
use crate::test::TestContext; use crate::test::TestContext;
#[cfg(not(test))] #[cfg(not(test))]
@@ -206,6 +210,10 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
..Default::default() ..Default::default()
}; };
// Start metrics
init_metrics();
tokio::spawn(async { metrics::serve().await });
let database = let database =
Pool::connect(&env::var("DATABASE_URL").expect("No database URL provided")).await.unwrap(); Pool::connect(&env::var("DATABASE_URL").expect("No database URL provided")).await.unwrap();
@@ -249,7 +257,7 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
match postman::initialize(kill_recv, ctx1, &pool1).await { match postman::initialize(kill_recv, ctx1, &pool1).await {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
error!("postman exiting: {}", e); panic!("postman exiting: {}", e);
} }
}; };
}); });
@@ -259,7 +267,7 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
if !run_settings.contains("web") { if !run_settings.contains("web") {
tokio::spawn(async move { tokio::spawn(async move {
reminder_web::initialize(kill_tx, ctx2, pool2).await.unwrap(); web::initialize(kill_tx, ctx2, pool2).await.unwrap();
}); });
} else { } else {
warn!("Not running web"); warn!("Not running web");
+36
View File
@@ -0,0 +1,36 @@
use axum::{routing::get, Router};
use lazy_static::lazy_static;
use log::warn;
use prometheus::{IntCounterVec, Opts, Registry};
lazy_static! {
pub static ref REGISTRY: Registry = Registry::new();
pub static ref REQUEST_COUNTER: IntCounterVec =
IntCounterVec::new(Opts::new("requests", "Requests"), &["method", "status", "route"])
.unwrap();
}
pub fn init_metrics() {
REGISTRY.register(Box::new(REQUEST_COUNTER.clone())).unwrap();
}
pub async fn serve() {
let app = Router::new().route("/metrics", get(metrics));
let listener = tokio::net::TcpListener::bind("localhost:31756").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn metrics() -> String {
let encoder = prometheus::TextEncoder::new();
let res_custom = encoder.encode_to_string(&REGISTRY.gather());
match res_custom {
Ok(s) => s,
Err(e) => {
warn!("Error encoding metrics: {:?}", e);
String::new()
}
}
}
+5 -4
View File
@@ -1,6 +1,6 @@
use std::collections::HashSet; use std::collections::HashSet;
use chrono::{Duration, NaiveDateTime, Utc}; use chrono::{DateTime, NaiveDateTime, TimeDelta, Utc};
use chrono_tz::Tz; use chrono_tz::Tz;
use poise::serenity_prelude::{ use poise::serenity_prelude::{
model::id::{ChannelId, GuildId, UserId}, model::id::{ChannelId, GuildId, UserId},
@@ -73,7 +73,7 @@ impl ReminderBuilder {
match queried_time.utc_time { match queried_time.utc_time {
Some(utc_time) => { Some(utc_time) => {
if utc_time < (Utc::now() - Duration::seconds(60)).naive_local() { if utc_time < (Utc::now() - TimeDelta::try_minutes(1).unwrap()).naive_local() {
Err(ReminderError::PastTime) Err(ReminderError::PastTime)
} else { } else {
sqlx::query!( sqlx::query!(
@@ -165,7 +165,7 @@ impl<'a> MultiReminderBuilder<'a> {
} }
pub fn time<T: Into<i64>>(mut self, time: T) -> Self { pub fn time<T: Into<i64>>(mut self, time: T) -> Self {
if let Some(utc_time) = NaiveDateTime::from_timestamp_opt(time.into(), 0) { if let Some(utc_time) = DateTime::from_timestamp(time.into(), 0).map(|d| d.naive_utc()) {
self.utc_time = utc_time; self.utc_time = utc_time;
} }
@@ -173,7 +173,8 @@ impl<'a> MultiReminderBuilder<'a> {
} }
pub fn expires<T: Into<i64>>(mut self, time: Option<T>) -> Self { pub fn expires<T: Into<i64>>(mut self, time: Option<T>) -> Self {
self.expires = time.map(|t| NaiveDateTime::from_timestamp_opt(t.into(), 0)).flatten(); self.expires =
time.map(|t| DateTime::from_timestamp(t.into(), 0)).flatten().map(|d| d.naive_utc());
self self
} }
+1
View File
@@ -38,6 +38,7 @@ use crate::{
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct Reminder { pub struct Reminder {
pub id: u32, pub id: u32,
pub uid: String, pub uid: String,
+1
View File
@@ -4,6 +4,7 @@ use sqlx::MySqlPool;
pub struct Timer { pub struct Timer {
pub name: String, pub name: String,
pub start_time: DateTime<Utc>, pub start_time: DateTime<Utc>,
#[allow(dead_code)]
pub owner: u64, pub owner: u64,
} }
+2 -1
View File
@@ -7,6 +7,7 @@ use crate::consts::LOCAL_TIMEZONE;
pub struct UserData { pub struct UserData {
pub id: u32, pub id: u32,
#[allow(dead_code)]
pub user: u64, pub user: u64,
pub dm_channel: u32, pub dm_channel: u32,
pub timezone: String, pub timezone: String,
@@ -22,7 +23,7 @@ impl UserData {
match sqlx::query!( match sqlx::query!(
" "
SELECT IFNULL(timezone, 'UTC') AS timezone FROM users WHERE user = ? SELECT IFNULL(timezone, 'UTC') AS timezone FROM users WHERE user = ?
", ",
user_id user_id
) )
+40
View File
@@ -0,0 +1,40 @@
use axum::{routing::get, Router};
use lazy_static;
use log::warn;
use prometheus::{register_int_counter, IntCounter, Registry};
lazy_static! {
static ref REGISTRY: Registry = Registry::new();
pub static ref REMINDERS_SENT: IntCounter =
register_int_counter!("reminders_sent", "Number of reminders sent").unwrap();
pub static ref REMINDERS_FAILED: IntCounter =
register_int_counter!("reminders_failed", "Number of reminders failed").unwrap();
}
pub fn init_metrics() {
REGISTRY.register(Box::new(REMINDERS_SENT.clone())).unwrap();
REGISTRY.register(Box::new(REMINDERS_FAILED.clone())).unwrap();
}
pub async fn serve() {
let app = Router::new().route("/metrics", get(metrics));
let metrics_port = std::env("PROMETHEUS_PORT").unwrap();
let listener =
tokio::net::TcpListener::bind(format!("localhost:{}", metrics_port)).await.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn metrics() -> String {
let encoder = prometheus::TextEncoder::new();
let res_custom = encoder.encode_to_string(&REGISTRY.gather());
match res_custom {
Ok(s) => s,
Err(e) => {
warn!("Error encoding metrics: {:?}", e);
String::new()
}
}
}
+1 -1
View File
@@ -3,7 +3,7 @@ mod sender;
use std::env; use std::env;
use log::{info, warn}; use log::{info, warn};
use serenity::client::Context; use poise::serenity_prelude::client::Context;
use sqlx::{Executor, MySql}; use sqlx::{Executor, MySql};
use tokio::{ use tokio::{
sync::broadcast::Receiver, sync::broadcast::Receiver,
@@ -1,13 +1,11 @@
use std::env; use std::env;
use chrono::{DateTime, Days, Duration, Months}; use chrono::{DateTime, Days, Months, TimeDelta};
use chrono_tz::Tz; use chrono_tz::Tz;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use log::{error, info, warn}; use log::{error, info, warn};
use num_integer::Integer; use num_integer::Integer;
use regex::{Captures, Regex}; use poise::serenity_prelude::{
use serde::Deserialize;
use serenity::{
all::{CreateAttachment, CreateEmbedFooter}, all::{CreateAttachment, CreateEmbedFooter},
builder::{CreateEmbed, CreateEmbedAuthor, CreateMessage, ExecuteWebhook}, builder::{CreateEmbed, CreateEmbedAuthor, CreateMessage, ExecuteWebhook},
http::{CacheHttp, Http, HttpError}, http::{CacheHttp, Http, HttpError},
@@ -18,6 +16,8 @@ use serenity::{
}, },
Error, Result, Error, Result,
}; };
use regex::{Captures, Regex};
use serde::Deserialize;
use sqlx::{ use sqlx::{
types::{ types::{
chrono::{NaiveDateTime, Utc}, chrono::{NaiveDateTime, Utc},
@@ -66,15 +66,15 @@ pub fn substitute(string: &str) -> String {
let format = caps.name("format").map(|m| m.as_str()); let format = caps.name("format").map(|m| m.as_str());
if let (Some(final_time), Some(format)) = (final_time, format) { if let (Some(final_time), Some(format)) = (final_time, format) {
match NaiveDateTime::from_timestamp_opt(final_time, 0) { match DateTime::from_timestamp(final_time, 0) {
Some(dt) => { Some(dt) => {
let now = Utc::now().naive_utc(); let now = Utc::now();
let difference = { let difference = {
if now < dt { if now < dt {
dt - Utc::now().naive_utc() dt - Utc::now()
} else { } else {
Utc::now().naive_utc() - dt Utc::now() - dt
} }
}; };
@@ -397,7 +397,13 @@ impl Reminder {
} }
if let Some(interval) = self.interval_seconds { if let Some(interval) = self.interval_seconds {
updated_reminder_time += Duration::seconds(interval as i64); updated_reminder_time += TimeDelta::try_seconds(interval as i64)
.unwrap_or_else(|| {
warn!("{}: Could not add {} seconds to a reminder", self.id, interval);
fail_count += 1;
TimeDelta::zero()
});
} }
} }
+2 -2
View File
@@ -1,9 +1,9 @@
use std::collections::HashMap; use std::collections::HashMap;
use rocket::serde::json::json; use rocket::{catch, serde::json::json};
use rocket_dyn_templates::Template; use rocket_dyn_templates::Template;
use crate::JsonValue; use crate::web::JsonValue;
#[catch(403)] #[catch(403)]
pub(crate) async fn forbidden() -> Template { pub(crate) async fn forbidden() -> Template {
+2 -2
View File
@@ -20,14 +20,14 @@ pub const DAY: usize = 24 * HOUR;
pub const CHARACTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; pub const CHARACTERS: &str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
use std::{collections::HashSet, env, iter::FromIterator}; use std::{collections::HashSet, env};
use lazy_static::lazy_static; use lazy_static::lazy_static;
use serenity::builder::CreateAttachment; use serenity::builder::CreateAttachment;
lazy_static! { lazy_static! {
pub static ref DEFAULT_AVATAR: CreateAttachment = CreateAttachment::bytes( pub static ref DEFAULT_AVATAR: CreateAttachment = CreateAttachment::bytes(
include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/../assets/webhook.jpg")) as &[u8], include_bytes!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/webhook.jpg")) as &[u8],
"webhook.jpg", "webhook.jpg",
); );
pub static ref SUBSCRIPTION_ROLES: HashSet<u64> = HashSet::from_iter( pub static ref SUBSCRIPTION_ROLES: HashSet<u64> = HashSet::from_iter(
File renamed without changes.
@@ -20,6 +20,7 @@ impl Transaction<'_> {
} }
#[derive(Debug)] #[derive(Debug)]
#[allow(dead_code)]
pub enum TransactionError { pub enum TransactionError {
Error(sqlx::Error), Error(sqlx::Error),
Missing, Missing,
File renamed without changes.
+27
View File
@@ -0,0 +1,27 @@
use rocket::{
fairing::{Fairing, Info, Kind},
Request, Response,
};
use crate::metrics::REQUEST_COUNTER;
pub struct MetricProducer;
#[rocket::async_trait]
impl Fairing for MetricProducer {
fn info(&self) -> Info {
Info { name: "Metrics fairing", kind: Kind::Response }
}
async fn on_response<'r>(&self, req: &'r Request<'_>, resp: &mut Response<'r>) {
if let Some(route) = req.route() {
REQUEST_COUNTER
.with_label_values(&[
req.method().as_str(),
&resp.status().code.to_string(),
&route.uri.to_string(),
])
.inc();
}
}
}
+14 -18
View File
@@ -1,6 +1,3 @@
#[macro_use]
extern crate rocket;
mod consts; mod consts;
#[macro_use] #[macro_use]
mod macros; mod macros;
@@ -11,24 +8,27 @@ mod routes;
use std::{env, path::Path}; use std::{env, path::Path};
use log::{error, info, warn};
use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl}; use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl};
use rocket::{ use poise::serenity_prelude::{
fs::FileServer,
http::CookieJar,
serde::json::{json, Value as JsonValue},
tokio::sync::broadcast::Sender,
};
use rocket_dyn_templates::Template;
use serenity::{
client::Context, client::Context,
http::CacheHttp, http::CacheHttp,
model::id::{GuildId, UserId}, model::id::{GuildId, UserId},
}; };
use rocket::{
catchers,
fs::FileServer,
http::CookieJar,
routes,
serde::json::{json, Value as JsonValue},
tokio::sync::broadcast::Sender,
};
use rocket_dyn_templates::Template;
use sqlx::{MySql, Pool}; use sqlx::{MySql, Pool};
use crate::{ use crate::web::{
consts::{CNC_GUILD, DISCORD_OAUTH_AUTHORIZE, DISCORD_OAUTH_TOKEN, SUBSCRIPTION_ROLES}, consts::{CNC_GUILD, DISCORD_OAUTH_AUTHORIZE, DISCORD_OAUTH_TOKEN, SUBSCRIPTION_ROLES},
metrics::{init_metrics, MetricProducer}, metrics::MetricProducer,
}; };
type Database = MySql; type Database = MySql;
@@ -68,9 +68,7 @@ pub async fn initialize(
let reqwest_client = reqwest::Client::new(); let reqwest_client = reqwest::Client::new();
let static_path = let static_path =
if Path::new("web/static").exists() { "web/static" } else { "/lib/reminder-rs/static" }; if Path::new("static").exists() { "static" } else { "/lib/reminder-rs/static" };
init_metrics();
rocket::build() rocket::build()
.attach(MetricProducer) .attach(MetricProducer)
@@ -96,7 +94,6 @@ pub async fn initialize(
routes![ routes![
routes::cookies, routes::cookies,
routes::index, routes::index,
routes::metrics::metrics,
routes::privacy, routes::privacy,
routes::report::report_error, routes::report::report_error,
routes::return_to_same_site, routes::return_to_same_site,
@@ -154,7 +151,6 @@ pub async fn initialize(
routes::dashboard::export::import_todos, routes::dashboard::export::import_todos,
], ],
) )
.mount("/admin", routes![routes::admin::admin_dashboard_home, routes::admin::bot_data])
.launch() .launch()
.await?; .await?;
@@ -1,4 +1,4 @@
use rocket::{http::CookieJar, serde::json::json, State}; use rocket::{get, http::CookieJar, serde::json::json, State};
use serde::Serialize; use serde::Serialize;
use serenity::{ use serenity::{
client::Context, client::Context,
@@ -8,7 +8,7 @@ use serenity::{
}, },
}; };
use crate::{check_authorization, routes::JsonResult}; use crate::web::{check_authorization, routes::JsonResult};
#[derive(Serialize)] #[derive(Serialize)]
struct ChannelInfo { struct ChannelInfo {
@@ -7,7 +7,7 @@ use std::env;
pub use channels::*; pub use channels::*;
pub use reminders::*; pub use reminders::*;
use rocket::{http::CookieJar, serde::json::json, State}; use rocket::{get, http::CookieJar, serde::json::json, State};
pub use roles::*; pub use roles::*;
use serenity::{ use serenity::{
client::Context, client::Context,
@@ -15,7 +15,7 @@ use serenity::{
}; };
pub use templates::*; pub use templates::*;
use crate::{check_authorization, routes::JsonResult}; use crate::web::{check_authorization, routes::JsonResult};
#[get("/api/guild/<id>")] #[get("/api/guild/<id>")]
pub async fn get_guild_info(id: u64, cookies: &CookieJar<'_>, ctx: &State<Context>) -> JsonResult { pub async fn get_guild_info(id: u64, cookies: &CookieJar<'_>, ctx: &State<Context>) -> JsonResult {
@@ -1,5 +1,8 @@
use log::warn;
use rocket::{ use rocket::{
get,
http::CookieJar, http::CookieJar,
patch, post,
serde::json::{json, Json}, serde::json::{json, Json},
State, State,
}; };
@@ -9,7 +12,7 @@ use serenity::{
}; };
use sqlx::{MySql, Pool}; use sqlx::{MySql, Pool};
use crate::{ use crate::web::{
check_authorization, check_guild_subscription, check_subscription, check_authorization, check_guild_subscription, check_subscription,
consts::MIN_INTERVAL, consts::MIN_INTERVAL,
guards::transaction::Transaction, guards::transaction::Transaction,
@@ -1,8 +1,9 @@
use rocket::{http::CookieJar, serde::json::json, State}; use log::warn;
use rocket::{get, http::CookieJar, serde::json::json, State};
use serde::Serialize; use serde::Serialize;
use serenity::client::Context; use serenity::client::Context;
use crate::{check_authorization, routes::JsonResult}; use crate::web::{check_authorization, routes::JsonResult};
#[derive(Serialize)] #[derive(Serialize)]
struct RoleInfo { struct RoleInfo {
@@ -1,12 +1,15 @@
use log::warn;
use rocket::{ use rocket::{
delete, get,
http::CookieJar, http::CookieJar,
post,
serde::json::{json, Json}, serde::json::{json, Json},
State, State,
}; };
use serenity::client::Context; use serenity::client::Context;
use sqlx::{MySql, Pool}; use sqlx::{MySql, Pool};
use crate::{ use crate::web::{
check_authorization, check_authorization,
consts::{ consts::{
MAX_CONTENT_LENGTH, MAX_EMBED_AUTHOR_LENGTH, MAX_EMBED_DESCRIPTION_LENGTH, MAX_CONTENT_LENGTH, MAX_EMBED_AUTHOR_LENGTH, MAX_EMBED_DESCRIPTION_LENGTH,
@@ -1,14 +1,16 @@
pub mod guild; pub mod guild;
pub mod user; pub mod user;
use log::warn;
use rocket::{ use rocket::{
delete,
http::CookieJar, http::CookieJar,
serde::json::{json, Json}, serde::json::{json, Json},
State, State,
}; };
use sqlx::{MySql, Pool}; use sqlx::{MySql, Pool};
use crate::routes::{dashboard::DeleteReminder, JsonResult}; use crate::web::routes::{dashboard::DeleteReminder, JsonResult};
#[delete("/api/reminders", data = "<reminder>")] #[delete("/api/reminders", data = "<reminder>")]
pub async fn delete_reminder( pub async fn delete_reminder(
@@ -1,5 +1,7 @@
use log::warn;
use reqwest::Client; use reqwest::Client;
use rocket::{ use rocket::{
get,
http::CookieJar, http::CookieJar,
serde::json::{json, Value as JsonValue}, serde::json::{json, Value as JsonValue},
State, State,
@@ -7,7 +9,7 @@ use rocket::{
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serenity::model::{id::GuildId, permissions::Permissions}; use serenity::model::{id::GuildId, permissions::Permissions};
use crate::consts::DISCORD_API; use crate::web::consts::DISCORD_API;
#[derive(Serialize)] #[derive(Serialize)]
struct GuildInfo { struct GuildInfo {
@@ -8,7 +8,9 @@ use chrono_tz::Tz;
pub use guilds::*; pub use guilds::*;
pub use reminders::*; pub use reminders::*;
use rocket::{ use rocket::{
get,
http::CookieJar, http::CookieJar,
patch,
serde::json::{json, Json, Value as JsonValue}, serde::json::{json, Json, Value as JsonValue},
State, State,
}; };
@@ -1,11 +1,12 @@
use chrono::{naive::NaiveDateTime, Utc}; use chrono::{naive::NaiveDateTime, Utc};
use futures::TryFutureExt; use futures::TryFutureExt;
use log::warn;
use rocket::serde::json::json; use rocket::serde::json::json;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serenity::{client::Context, futures, model::id::UserId}; use serenity::{client::Context, model::id::UserId};
use sqlx::types::Json; use sqlx::types::Json;
use crate::{ use crate::web::{
check_subscription, check_subscription,
consts::{ consts::{
DAY, MAX_CONTENT_LENGTH, MAX_EMBED_AUTHOR_LENGTH, MAX_EMBED_DESCRIPTION_LENGTH, DAY, MAX_CONTENT_LENGTH, MAX_EMBED_AUTHOR_LENGTH, MAX_EMBED_DESCRIPTION_LENGTH,
@@ -15,10 +16,7 @@ use crate::{
}, },
guards::transaction::Transaction, guards::transaction::Transaction,
routes::{ routes::{
dashboard::{ dashboard::{create_database_channel, generate_uid, name_default, Attachment, EmbedField},
create_database_channel, deserialize_optional_field, generate_uid, interval_default,
name_default, Attachment, EmbedField, Unset,
},
JsonResult, JsonResult,
}, },
Error, Error,
@@ -231,60 +229,3 @@ pub async fn create_reminder(
} }
} }
} }
#[derive(Deserialize)]
pub struct PatchReminder {
pub uid: String,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub attachment: Unset<Option<Attachment>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub attachment_name: Unset<Option<String>>,
#[serde(default)]
pub content: Unset<String>,
#[serde(default)]
pub embed_author: Unset<String>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub embed_author_url: Unset<Option<String>>,
#[serde(default)]
pub embed_color: Unset<u32>,
#[serde(default)]
pub embed_description: Unset<String>,
#[serde(default)]
pub embed_footer: Unset<String>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub embed_footer_url: Unset<Option<String>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub embed_image_url: Unset<Option<String>>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub embed_thumbnail_url: Unset<Option<String>>,
#[serde(default)]
pub embed_title: Unset<String>,
#[serde(default)]
pub embed_fields: Unset<Json<Vec<EmbedField>>>,
#[serde(default)]
pub enabled: Unset<bool>,
#[serde(default)]
#[serde(deserialize_with = "deserialize_optional_field")]
pub expires: Unset<Option<NaiveDateTime>>,
#[serde(default = "interval_default")]
#[serde(deserialize_with = "deserialize_optional_field")]
pub interval_seconds: Unset<Option<u32>>,
#[serde(default = "interval_default")]
#[serde(deserialize_with = "deserialize_optional_field")]
pub interval_days: Unset<Option<u32>>,
#[serde(default = "interval_default")]
#[serde(deserialize_with = "deserialize_optional_field")]
pub interval_months: Unset<Option<u32>>,
#[serde(default)]
pub name: Unset<String>,
#[serde(default)]
pub tts: Unset<bool>,
#[serde(default)]
pub utc_time: Unset<NaiveDateTime>,
}
@@ -1,12 +1,15 @@
use log::warn;
use rocket::{ use rocket::{
get,
http::CookieJar, http::CookieJar,
patch, post,
serde::json::{json, Json}, serde::json::{json, Json},
State, State,
}; };
use serenity::{client::Context, model::id::UserId}; use serenity::{client::Context, model::id::UserId};
use sqlx::{MySql, Pool}; use sqlx::{MySql, Pool};
use crate::{ use crate::web::{
check_subscription, check_subscription,
guards::transaction::Transaction, guards::transaction::Transaction,
routes::{ routes::{
@@ -1,7 +1,11 @@
use base64::{prelude::BASE64_STANDARD, Engine};
use csv::{QuoteStyle, WriterBuilder}; use csv::{QuoteStyle, WriterBuilder};
use log::warn;
use rocket::{ use rocket::{
get,
http::CookieJar, http::CookieJar,
serde::json::{json, serde_json, Json}, put,
serde::json::{json, Json},
State, State,
}; };
use serenity::{ use serenity::{
@@ -10,7 +14,7 @@ use serenity::{
}; };
use sqlx::{MySql, Pool}; use sqlx::{MySql, Pool};
use crate::{ use crate::web::{
check_authorization, check_authorization,
guards::transaction::Transaction, guards::transaction::Transaction,
routes::{ routes::{
@@ -134,7 +138,7 @@ pub(crate) async fn import_reminders(
let user_id = let user_id =
cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten().unwrap(); cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten().unwrap();
match base64::decode(&body.body) { match BASE64_STANDARD.decode(&body.body) {
Ok(body) => { Ok(body) => {
let mut reader = csv::Reader::from_reader(body.as_slice()); let mut reader = csv::Reader::from_reader(body.as_slice());
let mut count = 0; let mut count = 0;
@@ -292,7 +296,7 @@ pub async fn import_todos(
let channels_res = GuildId::new(id).channels(&ctx.inner()).await; let channels_res = GuildId::new(id).channels(&ctx.inner()).await;
match channels_res { match channels_res {
Ok(channels) => match base64::decode(&body.body) { Ok(channels) => match BASE64_STANDARD.decode(&body.body) {
Ok(body) => { Ok(body) => {
let mut reader = csv::Reader::from_reader(body.as_slice()); let mut reader = csv::Reader::from_reader(body.as_slice());
@@ -1,8 +1,12 @@
use std::path::Path; use std::path::Path;
use base64::{prelude::BASE64_STANDARD, Engine};
use chrono::{naive::NaiveDateTime, Utc}; use chrono::{naive::NaiveDateTime, Utc};
use log::warn;
use rand::{rngs::OsRng, seq::IteratorRandom}; use rand::{rngs::OsRng, seq::IteratorRandom};
use rocket::{fs::NamedFile, http::CookieJar, response::Redirect, serde::json::json}; use rocket::{
fs::NamedFile, get, http::CookieJar, response::Redirect, serde::json::json, Responder,
};
use rocket_dyn_templates::Template; use rocket_dyn_templates::Template;
use secrecy::ExposeSecret; use secrecy::ExposeSecret;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
@@ -14,7 +18,7 @@ use serenity::{
}; };
use sqlx::types::Json; use sqlx::types::Json;
use crate::{ use crate::web::{
catchers::internal_server_error, catchers::internal_server_error,
check_guild_subscription, check_subscription, check_guild_subscription, check_subscription,
consts::{ consts::{
@@ -63,7 +67,7 @@ impl<'de> Deserialize<'de> for Attachment {
D: Deserializer<'de>, D: Deserializer<'de>,
{ {
let string = String::deserialize(deserializer)?; let string = String::deserialize(deserializer)?;
Ok(Attachment(base64::decode(string).map_err(de::Error::custom)?)) Ok(Attachment(BASE64_STANDARD.decode(string).map_err(de::Error::custom)?))
} }
} }
@@ -72,7 +76,7 @@ impl Serialize for Attachment {
where where
S: Serializer, S: Serializer,
{ {
serializer.collect_str(&base64::encode(&self.0)) serializer.collect_str(&BASE64_STANDARD.encode(&self.0))
} }
} }
@@ -595,7 +599,7 @@ async fn create_database_channel(
ctx: impl CacheHttp, ctx: impl CacheHttp,
channel: ChannelId, channel: ChannelId,
transaction: &mut Transaction<'_>, transaction: &mut Transaction<'_>,
) -> Result<u32, crate::Error> { ) -> Result<u32, Error> {
let row = sqlx::query!( let row = sqlx::query!(
"SELECT webhook_token, webhook_id FROM channels WHERE channel = ?", "SELECT webhook_token, webhook_id FROM channels WHERE channel = ?",
channel.get() channel.get()
@@ -5,13 +5,14 @@ use oauth2::{
}; };
use reqwest::Client; use reqwest::Client;
use rocket::{ use rocket::{
get,
http::{private::cookie::Expiration, Cookie, CookieJar, SameSite}, http::{private::cookie::Expiration, Cookie, CookieJar, SameSite},
response::{Flash, Redirect}, response::{Flash, Redirect},
uri, State, uri, State,
}; };
use serenity::model::user::User; use serenity::model::user::User;
use crate::{consts::DISCORD_API, routes}; use crate::web::{consts::DISCORD_API, routes};
#[get("/discord")] #[get("/discord")]
pub async fn discord_login( pub async fn discord_login(
@@ -1,12 +1,10 @@
pub mod admin;
pub mod dashboard; pub mod dashboard;
pub mod login; pub mod login;
pub mod metrics;
pub mod report; pub mod report;
use std::collections::HashMap; use std::collections::HashMap;
use rocket::{request::FlashMessage, serde::json::Value as JsonValue}; use rocket::{get, request::FlashMessage, serde::json::Value as JsonValue};
use rocket_dyn_templates::Template; use rocket_dyn_templates::Template;
pub type JsonResult = Result<JsonValue, JsonValue>; pub type JsonResult = Result<JsonValue, JsonValue>;
@@ -1,12 +1,14 @@
use log::error;
use rocket::{ use rocket::{
http::CookieJar, http::CookieJar,
post,
serde::{ serde::{
json::{json, Json}, json::{json, Json},
Deserialize, Deserialize,
}, },
}; };
use crate::routes::JsonResult; use crate::web::routes::JsonResult;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct ClientError { pub struct ClientError {
View File
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

File renamed without changes.
File renamed without changes.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 762 B

File renamed without changes.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 323 KiB

After

Width:  |  Height:  |  Size: 323 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

File renamed without changes.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

+1
View File
@@ -0,0 +1 @@
/home/jude/reminder-bot/reminder-dashboard/dist/index.html
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

Before

Width:  |  Height:  |  Size: 712 KiB

After

Width:  |  Height:  |  Size: 712 KiB

File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 152 files, more files were not shown because too many files have changed in this diff. Show more