From e78c43ab6248d11ec999dc296d3f337c6d0b9aa0 Mon Sep 17 00:00:00 2001 From: trisua Date: Wed, 23 Jul 2025 14:44:47 -0400 Subject: [PATCH] add: check ip ban endpoint --- Cargo.lock | 2 +- crates/app/src/public/js/app_sdk.js | 20 ++++++++++++++++ crates/app/src/routes/api/v1/auth/ipbans.rs | 26 +++++++++++++++++++-- crates/app/src/routes/api/v1/mod.rs | 1 + crates/core/Cargo.toml | 2 +- crates/core/src/database/questions.rs | 4 ++++ crates/core/src/sdk.rs | 16 +++++++++++++ crates/shared/src/markdown.rs | 3 +-- 8 files changed, 68 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ecaa2f8..b5148b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3320,7 +3320,7 @@ dependencies = [ [[package]] name = "tetratto-core" -version = "12.0.0" +version = "12.0.1" dependencies = [ "async-recursion", "base16ct", diff --git a/crates/app/src/public/js/app_sdk.js b/crates/app/src/public/js/app_sdk.js index cd21e6a..7a6c834 100644 --- a/crates/app/src/public/js/app_sdk.js +++ b/crates/app/src/public/js/app_sdk.js @@ -72,6 +72,25 @@ export default function tetratto({ ); } + async function check_ip(ip) { + if (!api_key) { + throw Error("No API key provided."); + } + + return api_promise( + json_parse( + await ( + await fetch(`${host}/api/v1/bans/${ip}`, { + method: "GET", + headers: { + "Atto-Secret-Key": api_key, + }, + }) + ).text(), + ), + ); + } + async function query(body) { if (!api_key) { throw Error("No API key provided."); @@ -285,6 +304,7 @@ export default function tetratto({ api_key, // app data app, + check_ip, query, insert, update, diff --git a/crates/app/src/routes/api/v1/auth/ipbans.rs b/crates/app/src/routes/api/v1/auth/ipbans.rs index 7163091..a8eb856 100644 --- a/crates/app/src/routes/api/v1/auth/ipbans.rs +++ b/crates/app/src/routes/api/v1/auth/ipbans.rs @@ -1,12 +1,34 @@ use crate::{ - State, get_user_from_token, + get_app_from_key, get_user_from_token, model::{ApiReturn, Error}, routes::api::v1::CreateIpBan, + State, }; -use axum::{Extension, Json, extract::Path, response::IntoResponse}; +use axum::{extract::Path, http::HeaderMap, response::IntoResponse, Extension, Json}; use crate::cookie::CookieJar; use tetratto_core::model::{addr::RemoteAddr, auth::IpBan, permissions::FinePermission}; +/// Check if the given IP is banned. +pub async fn check_request( + headers: HeaderMap, + Path(ip): Path, + Extension(data): Extension, +) -> impl IntoResponse { + let data = &(data.read().await).0; + if get_app_from_key!(data, headers).is_none() { + return Json(Error::NotAllowed.into()); + } + + Json(ApiReturn { + ok: true, + message: "Success".to_string(), + payload: data + .get_ipban_by_addr(&RemoteAddr::from(ip.as_str())) + .await + .is_ok(), + }) +} + /// Create a new IP ban. pub async fn create_request( jar: CookieJar, diff --git a/crates/app/src/routes/api/v1/mod.rs b/crates/app/src/routes/api/v1/mod.rs index 91f4bfa..8a5d95a 100644 --- a/crates/app/src/routes/api/v1/mod.rs +++ b/crates/app/src/routes/api/v1/mod.rs @@ -495,6 +495,7 @@ pub fn routes() -> Router { post(communities::communities::update_membership_role), ) // ipbans + .route("/bans/{ip}", get(auth::ipbans::check_request)) .route("/bans/{ip}", post(auth::ipbans::create_request)) .route("/bans/{ip}", delete(auth::ipbans::delete_request)) // reports diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 98a0947..71faf6c 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "tetratto-core" description = "The core behind Tetratto" -version = "12.0.0" +version = "12.0.1" edition = "2024" authors.workspace = true repository.workspace = true diff --git a/crates/core/src/database/questions.rs b/crates/core/src/database/questions.rs index 7722250..3703d4f 100644 --- a/crates/core/src/database/questions.rs +++ b/crates/core/src/database/questions.rs @@ -408,6 +408,10 @@ impl DataManager { // check muted phrases for phrase in receiver.settings.muted { + if phrase.is_empty() { + continue; + } + if data.content.contains(&phrase) { // act like the question was created so theyre less likely to try and send it again or bypass return Ok(0); diff --git a/crates/core/src/sdk.rs b/crates/core/src/sdk.rs index 71ba502..0e5add6 100644 --- a/crates/core/src/sdk.rs +++ b/crates/core/src/sdk.rs @@ -75,6 +75,22 @@ impl DataClient { } } + /// Check if the given IP is IP banned from the Tetratto host. You will only know + /// if the IP is banned or not, meaning you will not be shown the reason if it + /// is banned. + pub async fn check_ip(&self, ip: &str) -> Result { + match self + .http + .get(format!("{}/api/v1/bans/{}", self.host, ip)) + .header("Atto-Secret-Key", &self.api_key) + .send() + .await + { + Ok(x) => api_return_ok!(bool, x), + Err(e) => Err(Error::MiscError(e.to_string())), + } + } + /// Query the app's data. pub async fn query(&self, query: &SimplifiedQuery) -> Result { match self diff --git a/crates/shared/src/markdown.rs b/crates/shared/src/markdown.rs index 6e574a8..b1bb625 100644 --- a/crates/shared/src/markdown.rs +++ b/crates/shared/src/markdown.rs @@ -31,9 +31,8 @@ pub fn clean_html(html: String, allowed_attributes: HashSet<&str>) -> String { .rm_tags(&["script", "style", "link", "canvas"]) .add_tag_attributes("a", &["href", "target"]) .add_url_schemes(&["atto"]) - .clean(&html) + .clean(&html.replace("