add: user ban_reason
This commit is contained in:
parent
b25bda29b8
commit
d1c3643574
11 changed files with 100 additions and 9 deletions
|
@ -198,6 +198,7 @@ version = "1.0.0"
|
||||||
"mod_panel:label.associations" = "Associations"
|
"mod_panel:label.associations" = "Associations"
|
||||||
"mod_panel:label.invited_by" = "Invited by"
|
"mod_panel:label.invited_by" = "Invited by"
|
||||||
"mod_panel:label.send_debug_payload" = "Send debug payload"
|
"mod_panel:label.send_debug_payload" = "Send debug payload"
|
||||||
|
"mod_panel:label.ban_reason" = "Ban reason"
|
||||||
"mod_panel:action.send" = "Send"
|
"mod_panel:action.send" = "Send"
|
||||||
|
|
||||||
"requests:label.requests" = "Requests"
|
"requests:label.requests" = "Requests"
|
||||||
|
|
|
@ -87,7 +87,10 @@ macro_rules! get_user_from_token {
|
||||||
{
|
{
|
||||||
Ok(ua) => {
|
Ok(ua) => {
|
||||||
if ua.permissions.check_banned() {
|
if ua.permissions.check_banned() {
|
||||||
Some(tetratto_core::model::auth::User::banned())
|
let mut banned_user = tetratto_core::model::auth::User::banned();
|
||||||
|
banned_user.ban_reason = ua.ban_reason;
|
||||||
|
|
||||||
|
Some(banned_user)
|
||||||
} else {
|
} else {
|
||||||
Some(ua)
|
Some(ua)
|
||||||
}
|
}
|
||||||
|
|
|
@ -102,7 +102,11 @@
|
||||||
("class" "flush")
|
("class" "flush")
|
||||||
("style" "font-weight: 600")
|
("style" "font-weight: 600")
|
||||||
("target" "_top")
|
("target" "_top")
|
||||||
(text "{{ self::username(user=user) }}"))
|
(text "{% if user.permissions|has_banned -%}")
|
||||||
|
(del ("class" "fade") (text "{{ self::username(user=user) }}"))
|
||||||
|
(text "{% else %}")
|
||||||
|
(text "{{ self::username(user=user) }}")
|
||||||
|
(text "{%- endif %}"))
|
||||||
(text "{{ self::online_indicator(user=user) }} {% if user.is_verified -%}")
|
(text "{{ self::online_indicator(user=user) }} {% if user.is_verified -%}")
|
||||||
(span
|
(span
|
||||||
("title" "Verified")
|
("title" "Verified")
|
||||||
|
|
|
@ -84,7 +84,7 @@
|
||||||
const ui = await ns(\"ui\");
|
const ui = await ns(\"ui\");
|
||||||
const element = document.getElementById(\"mod_options\");
|
const element = document.getElementById(\"mod_options\");
|
||||||
|
|
||||||
async function profile_request(do_confirm, path, body) {
|
globalThis.profile_request = async (do_confirm, path, body) => {
|
||||||
if (do_confirm) {
|
if (do_confirm) {
|
||||||
if (
|
if (
|
||||||
!(await trigger(\"atto::confirm\", [
|
!(await trigger(\"atto::confirm\", [
|
||||||
|
@ -273,6 +273,33 @@
|
||||||
("class" "card lowered flex flex-wrap gap-2")
|
("class" "card lowered flex flex-wrap gap-2")
|
||||||
(text "{{ components::user_plate(user=invite[0], show_menu=false) }}")))
|
(text "{{ components::user_plate(user=invite[0], show_menu=false) }}")))
|
||||||
(text "{%- endif %}")
|
(text "{%- endif %}")
|
||||||
|
(div
|
||||||
|
("class" "card-nest w-full")
|
||||||
|
(div
|
||||||
|
("class" "card small flex items-center justify-between gap-2")
|
||||||
|
(div
|
||||||
|
("class" "flex items-center gap-2")
|
||||||
|
(icon (text "scale"))
|
||||||
|
(span
|
||||||
|
(str (text "mod_panel:label.ban_reason")))))
|
||||||
|
(form
|
||||||
|
("class" "card flex flex-col gap-2")
|
||||||
|
("onsubmit" "event.preventDefault(); profile_request(false, 'ban_reason', { reason: event.target.reason.value || '' })")
|
||||||
|
(div
|
||||||
|
("class" "flex flex-col gap-1")
|
||||||
|
(label
|
||||||
|
("for" "title")
|
||||||
|
(str (text "mod_panel:label.ban_reason")))
|
||||||
|
(textarea
|
||||||
|
("type" "text")
|
||||||
|
("name" "reason")
|
||||||
|
("id" "reason")
|
||||||
|
("placeholder" "ban reason")
|
||||||
|
("minlength" "2")
|
||||||
|
(text "{{ profile.ban_reason|remove_script_tags|safe }}")))
|
||||||
|
(button
|
||||||
|
("class" "primary")
|
||||||
|
(str (text "general:action.save")))))
|
||||||
(div
|
(div
|
||||||
("class" "card-nest w-full")
|
("class" "card-nest w-full")
|
||||||
(div
|
(div
|
||||||
|
|
|
@ -70,8 +70,13 @@
|
||||||
(str (text "general:label.account_banned")))
|
(str (text "general:label.account_banned")))
|
||||||
|
|
||||||
(div
|
(div
|
||||||
("class" "card")
|
("class" "card flex flex-col gap-2 no_p_margin")
|
||||||
(str (text "general:label.account_banned_body"))))))
|
(str (text "general:label.account_banned_body"))
|
||||||
|
(hr)
|
||||||
|
(span ("class" "fade") (text "The following reason was provided by a moderator:"))
|
||||||
|
(div
|
||||||
|
("class" "card lowered w-full")
|
||||||
|
(text "{{ user.ban_reason|markdown|safe }}"))))))
|
||||||
|
|
||||||
; if we aren't banned, just show the page body
|
; if we aren't banned, just show the page body
|
||||||
(text "{% elif user and user.awaiting_purchase %}")
|
(text "{% elif user and user.awaiting_purchase %}")
|
||||||
|
|
|
@ -4,8 +4,9 @@ use crate::{
|
||||||
model::{ApiReturn, Error},
|
model::{ApiReturn, Error},
|
||||||
routes::api::v1::{
|
routes::api::v1::{
|
||||||
AppendAssociations, AwardAchievement, DeleteUser, DisableTotp, RefreshGrantToken,
|
AppendAssociations, AwardAchievement, DeleteUser, DisableTotp, RefreshGrantToken,
|
||||||
UpdateSecondaryUserRole, UpdateUserAwaitingPurchase, UpdateUserInviteCode,
|
UpdateSecondaryUserRole, UpdateUserAwaitingPurchase, UpdateUserBanReason,
|
||||||
UpdateUserIsVerified, UpdateUserPassword, UpdateUserRole, UpdateUserUsername,
|
UpdateUserInviteCode, UpdateUserIsVerified, UpdateUserPassword, UpdateUserRole,
|
||||||
|
UpdateUserUsername,
|
||||||
},
|
},
|
||||||
State,
|
State,
|
||||||
};
|
};
|
||||||
|
@ -424,6 +425,35 @@ pub async fn update_user_secondary_role_request(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update the ban reason of the given user.
|
||||||
|
///
|
||||||
|
/// Does not support third-party grants.
|
||||||
|
pub async fn update_user_ban_reason_request(
|
||||||
|
jar: CookieJar,
|
||||||
|
Path(id): Path<usize>,
|
||||||
|
Extension(data): Extension<State>,
|
||||||
|
Json(req): Json<UpdateUserBanReason>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let data = &(data.read().await).0;
|
||||||
|
let user = match get_user_from_token!(jar, data) {
|
||||||
|
Some(ua) => ua,
|
||||||
|
None => return Json(Error::NotAllowed.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !user.permissions.check(FinePermission::MANAGE_USERS) {
|
||||||
|
return Json(Error::NotAllowed.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
match data.update_user_ban_reason(id, &req.reason).await {
|
||||||
|
Ok(_) => Json(ApiReturn {
|
||||||
|
ok: true,
|
||||||
|
message: "User updated".to_string(),
|
||||||
|
payload: (),
|
||||||
|
}),
|
||||||
|
Err(e) => Json(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Update the current user's last seen value.
|
/// Update the current user's last seen value.
|
||||||
pub async fn seen_request(jar: CookieJar, Extension(data): Extension<State>) -> impl IntoResponse {
|
pub async fn seen_request(jar: CookieJar, Extension(data): Extension<State>) -> impl IntoResponse {
|
||||||
let data = &(data.read().await).0;
|
let data = &(data.read().await).0;
|
||||||
|
|
|
@ -322,6 +322,10 @@ pub fn routes() -> Router {
|
||||||
"/auth/user/{id}/role/2",
|
"/auth/user/{id}/role/2",
|
||||||
post(auth::profile::update_user_secondary_role_request),
|
post(auth::profile::update_user_secondary_role_request),
|
||||||
)
|
)
|
||||||
|
.route(
|
||||||
|
"/auth/user/{id}/ban_reason",
|
||||||
|
post(auth::profile::update_user_ban_reason_request),
|
||||||
|
)
|
||||||
.route(
|
.route(
|
||||||
"/auth/user/{id}",
|
"/auth/user/{id}",
|
||||||
delete(auth::profile::delete_user_request),
|
delete(auth::profile::delete_user_request),
|
||||||
|
@ -840,6 +844,11 @@ pub struct UpdateSecondaryUserRole {
|
||||||
pub role: SecondaryPermission,
|
pub role: SecondaryPermission,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct UpdateUserBanReason {
|
||||||
|
pub reason: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct UpdateUserInviteCode {
|
pub struct UpdateUserInviteCode {
|
||||||
pub invite_code: String,
|
pub invite_code: String,
|
||||||
|
|
|
@ -119,6 +119,7 @@ impl DataManager {
|
||||||
was_purchased: get!(x->25(i32)) as i8 == 1,
|
was_purchased: get!(x->25(i32)) as i8 == 1,
|
||||||
browser_session: get!(x->26(String)),
|
browser_session: get!(x->26(String)),
|
||||||
seller_data: serde_json::from_str(&get!(x->27(String)).to_string()).unwrap(),
|
seller_data: serde_json::from_str(&get!(x->27(String)).to_string()).unwrap(),
|
||||||
|
ban_reason: get!(x->28(String)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +276,7 @@ impl DataManager {
|
||||||
|
|
||||||
let res = execute!(
|
let res = execute!(
|
||||||
&conn,
|
&conn,
|
||||||
"INSERT INTO users VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28)",
|
"INSERT INTO users VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25, $26, $27, $28, $29)",
|
||||||
params![
|
params![
|
||||||
&(data.id as i64),
|
&(data.id as i64),
|
||||||
&(data.created as i64),
|
&(data.created as i64),
|
||||||
|
@ -305,6 +306,7 @@ impl DataManager {
|
||||||
&if data.was_purchased { 1_i32 } else { 0_i32 },
|
&if data.was_purchased { 1_i32 } else { 0_i32 },
|
||||||
&data.browser_session,
|
&data.browser_session,
|
||||||
&serde_json::to_string(&data.seller_data).unwrap(),
|
&serde_json::to_string(&data.seller_data).unwrap(),
|
||||||
|
&data.ban_reason
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1001,6 +1003,7 @@ impl DataManager {
|
||||||
auto_method!(update_user_invite_code(i64)@get_user_by_id -> "UPDATE users SET invite_code = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
|
auto_method!(update_user_invite_code(i64)@get_user_by_id -> "UPDATE users SET invite_code = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
|
||||||
auto_method!(update_user_browser_session(&str)@get_user_by_id -> "UPDATE users SET browser_session = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
|
auto_method!(update_user_browser_session(&str)@get_user_by_id -> "UPDATE users SET browser_session = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
|
||||||
auto_method!(update_user_seller_data(StripeSellerData)@get_user_by_id -> "UPDATE users SET seller_data = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
|
auto_method!(update_user_seller_data(StripeSellerData)@get_user_by_id -> "UPDATE users SET seller_data = $1 WHERE id = $2" --serde --cache-key-tmpl=cache_clear_user);
|
||||||
|
auto_method!(update_user_ban_reason(&str)@get_user_by_id -> "UPDATE users SET ban_reason = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
|
||||||
|
|
||||||
auto_method!(get_user_by_stripe_id(&str)@get_user_from_row -> "SELECT * FROM users WHERE stripe_id = $1" --name="user" --returns=User);
|
auto_method!(get_user_by_stripe_id(&str)@get_user_from_row -> "SELECT * FROM users WHERE stripe_id = $1" --name="user" --returns=User);
|
||||||
auto_method!(update_user_stripe_id(&str)@get_user_by_id -> "UPDATE users SET stripe_id = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
|
auto_method!(update_user_stripe_id(&str)@get_user_by_id -> "UPDATE users SET stripe_id = $1 WHERE id = $2" --cache-key-tmpl=cache_clear_user);
|
||||||
|
|
|
@ -20,9 +20,12 @@ CREATE TABLE IF NOT EXISTS users (
|
||||||
stripe_id TEXT NOT NULL,
|
stripe_id TEXT NOT NULL,
|
||||||
grants TEXT NOT NULL,
|
grants TEXT NOT NULL,
|
||||||
associated TEXT NOT NULL,
|
associated TEXT NOT NULL,
|
||||||
|
invite_code TEXT NOT NULL,
|
||||||
secondary_permissions INT NOT NULL,
|
secondary_permissions INT NOT NULL,
|
||||||
achievements TEXT NOT NULL,
|
achievements TEXT NOT NULL,
|
||||||
awaiting_purchase INT NOT NULL,
|
awaiting_purchase INT NOT NULL,
|
||||||
was_purchased INT NOT NULL,
|
was_purchased INT NOT NULL,
|
||||||
browser_session TEXT NOT NULL
|
browser_session TEXT NOT NULL,
|
||||||
|
seller_data TEXT NOT NULL,
|
||||||
|
ban_reason TEXT NOT NULL
|
||||||
)
|
)
|
||||||
|
|
|
@ -83,6 +83,9 @@ pub struct User {
|
||||||
/// Stripe connected account information (for Tetratto marketplace).
|
/// Stripe connected account information (for Tetratto marketplace).
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub seller_data: StripeSellerData,
|
pub seller_data: StripeSellerData,
|
||||||
|
/// The reason the user was banned.
|
||||||
|
#[serde(default)]
|
||||||
|
pub ban_reason: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type UserConnections =
|
pub type UserConnections =
|
||||||
|
@ -383,6 +386,7 @@ impl User {
|
||||||
was_purchased: false,
|
was_purchased: false,
|
||||||
browser_session: String::new(),
|
browser_session: String::new(),
|
||||||
seller_data: StripeSellerData::default(),
|
seller_data: StripeSellerData::default(),
|
||||||
|
ban_reason: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
sql_changes/users_ban_reason.sql
Normal file
2
sql_changes/users_ban_reason.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE users
|
||||||
|
ADD COLUMN ban_reason TEXT NOT NULL DEFAULT '';
|
Loading…
Add table
Add a link
Reference in a new issue