Tento commit je obsažen v:
sneedmaster 2023-12-13 16:17:49 +01:00
rodič 8a73452cc0
revize eb33ad9a19
6 změnil soubory, kde provedl 101 přidání a 2 odebrání

Zobrazit soubor

@ -68,4 +68,8 @@ pub struct BoardCfg {
pub require_thread_file: bool, pub require_thread_file: bool,
pub require_reply_content: bool, pub require_reply_content: bool,
pub require_reply_file: bool, pub require_reply_file: bool,
pub antispam: bool,
pub antispam_ip: i64,
pub antispam_content: i64,
pub antispam_both: i64,
} }

Zobrazit soubor

@ -76,6 +76,8 @@ pub enum NekrochanError {
AlreadyAppealedError, AlreadyAppealedError,
#[error("Tento ban nelze odvolat.")] #[error("Tento ban nelze odvolat.")]
UnappealableError, UnappealableError,
#[error("Tvůj příspěvek vypadá jako spam.")]
FloodError,
// 500 // 500
#[error("Nadnástěnka nebyla inicializována.")] #[error("Nadnástěnka nebyla inicializována.")]
OverboardError, OverboardError,
@ -235,6 +237,7 @@ impl ResponseError for NekrochanError {
| NekrochanError::IncorrectCaptchaError | NekrochanError::IncorrectCaptchaError
| NekrochanError::IncorrectPasswordError(_) => StatusCode::UNAUTHORIZED, | NekrochanError::IncorrectPasswordError(_) => StatusCode::UNAUTHORIZED,
NekrochanError::HeaderError(_) => StatusCode::BAD_GATEWAY, NekrochanError::HeaderError(_) => StatusCode::BAD_GATEWAY,
NekrochanError::FloodError => StatusCode::TOO_MANY_REQUESTS,
NekrochanError::OverboardError | NekrochanError::InternalError => { NekrochanError::OverboardError | NekrochanError::InternalError => {
StatusCode::INTERNAL_SERVER_ERROR StatusCode::INTERNAL_SERVER_ERROR
} }

Zobrazit soubor

@ -3,8 +3,11 @@ use actix_web::{
cookie::Cookie, http::StatusCode, post, web::Data, HttpRequest, HttpResponse, cookie::Cookie, http::StatusCode, post, web::Data, HttpRequest, HttpResponse,
HttpResponseBuilder, HttpResponseBuilder,
}; };
use chrono::{Duration, Utc};
use pwhash::bcrypt::hash; use pwhash::bcrypt::hash;
use redis::AsyncCommands;
use sha256::digest; use sha256::digest;
use std::{collections::HashSet, net::IpAddr};
use crate::{ use crate::{
ctx::Ctx, ctx::Ctx,
@ -171,6 +174,10 @@ pub async fn create_post(
) )
.await?; .await?;
if board.config.antispam {
check_spam(&ctx, &board, ip, content_nomarkup.clone()).await?;
}
if form.files.len() > board.config.0.file_limit { if form.files.len() > board.config.0.file_limit {
return Err(NekrochanError::FileLimitError(board.config.0.file_limit)); return Err(NekrochanError::FileLimitError(board.config.0.file_limit));
} }
@ -248,3 +255,53 @@ pub async fn create_post(
Ok(res) Ok(res)
} }
pub async fn check_spam(
ctx: &Ctx,
board: &Board,
ip: IpAddr,
content_nomarkup: String,
) -> Result<(), NekrochanError> {
let ip_key = format!("by_ip:{ip}");
let content_key = format!("by_content:{}", digest(content_nomarkup));
let antispam_ip = (Utc::now() - Duration::seconds(board.config.antispam_ip)).timestamp_micros();
let antispam_content =
(Utc::now() - Duration::seconds(board.config.antispam_content)).timestamp_micros();
let antispam_both =
(Utc::now() - Duration::seconds(board.config.antispam_both)).timestamp_micros();
let ip_posts: HashSet<String> = ctx
.cache()
.zrangebyscore(&ip_key, antispam_ip, "+inf")
.await?;
let content_posts: HashSet<String> = ctx
.cache()
.zrangebyscore(&content_key, antispam_content, "+inf")
.await?;
let ip_posts2: HashSet<String> = ctx
.cache()
.zrangebyscore(&ip_key, antispam_both, "+inf")
.await?;
let content_posts2: HashSet<String> = ctx
.cache()
.zrangebyscore(&content_key, antispam_both, "+inf")
.await?;
let both_posts = ip_posts2.intersection(&content_posts2);
if !ip_posts.is_empty() {
return Err(NekrochanError::FloodError);
}
if !content_posts.is_empty() {
return Err(NekrochanError::FloodError);
}
if both_posts.count() != 0 {
return Err(NekrochanError::FloodError);
}
Ok(())
}

Zobrazit soubor

@ -1,8 +1,7 @@
use std::fmt::Write;
use actix_web::{post, web::Data, HttpRequest, HttpResponse}; use actix_web::{post, web::Data, HttpRequest, HttpResponse};
use pwhash::bcrypt::verify; use pwhash::bcrypt::verify;
use serde::Deserialize; use serde::Deserialize;
use std::fmt::Write;
use crate::{ use crate::{
ctx::Ctx, ctx::Ctx,

Zobrazit soubor

@ -24,6 +24,10 @@ pub struct UpdateBoardConfigForm {
require_thread_file: Option<String>, require_thread_file: Option<String>,
require_reply_content: Option<String>, require_reply_content: Option<String>,
require_reply_file: Option<String>, require_reply_file: Option<String>,
antispam: Option<String>,
antispam_ip: i64,
antispam_content: i64,
antispam_both: i64,
} }
#[post("/staff/actions/update-board-config")] #[post("/staff/actions/update-board-config")]
@ -58,6 +62,10 @@ pub async fn update_board_config(
let require_thread_file = form.require_thread_file.is_some(); let require_thread_file = form.require_thread_file.is_some();
let require_reply_content = form.require_reply_content.is_some(); let require_reply_content = form.require_reply_content.is_some();
let require_reply_file = form.require_reply_file.is_some(); let require_reply_file = form.require_reply_file.is_some();
let antispam = form.antispam.is_some();
let antispam_ip = form.antispam_ip;
let antispam_content = form.antispam_content;
let antispam_both = form.antispam_both;
let config = BoardCfg { let config = BoardCfg {
anon_name, anon_name,
@ -75,6 +83,10 @@ pub async fn update_board_config(
require_thread_file, require_thread_file,
require_reply_content, require_reply_content,
require_reply_file, require_reply_file,
antispam,
antispam_ip,
antispam_content,
antispam_both
}; };
board.update_config(&ctx, config).await?; board.update_config(&ctx, config).await?;

Zobrazit soubor

@ -145,6 +145,30 @@
</td> </td>
</tr> </tr>
<tr>
<td class="label">Antispam</td>
<td>
<div class="input-wrapper">
<input name="antispam" type="checkbox" {% if board.config.0.antispam %}checked="checked"{% endif %}>
</div>
</td>
</tr>
<tr>
<td class="label">Interval antispamu (IP)</td>
<td><input name="antispam_ip" type="number" min="0" value="{{ board.config.0.antispam_ip }}" required="required"></td>
</tr>
<tr>
<td class="label">Interval antispamu (Obsah)</td>
<td><input name="antispam_content" type="number" min="0" value="{{ board.config.0.antispam_content }}" required="required"></td>
</tr>
<tr>
<td class="label">Interval antispamu (IP+Obsah)</td>
<td><input name="antispam_both" type="number" min="0" value="{{ board.config.0.antispam_both }}" required="required"></td>
</tr>
<tr> <tr>
<td class="submit" colspan="2"><input class="button" type="submit" value="Uložit"></td> <td class="submit" colspan="2"><input class="button" type="submit" value="Uložit"></td>
</tr> </tr>