mirror of
https://git.ngram.ca/OpenJam/rc-servers
synced 2026-08-23 23:08:52 +00:00
Add minimum chat moderation to enable bans for #22
This commit is contained in:
47
rc_chat_room/src/operations/add_modify_sanction.rs
Normal file
47
rc_chat_room/src/operations/add_modify_sanction.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||
use polariton::operation::{ParameterTable, Typed};
|
||||
|
||||
const CODE: u8 = 7;
|
||||
|
||||
const SANCTION_TY_PARAM_KEY: u8 = 9; // int; in
|
||||
const IS_ADDING_PARAM_KEY: u8 = 10; // bool; in
|
||||
const DURATION_PARAM_KEY: u8 = 11; // int; in
|
||||
const REASON_PARAM_KEY: u8 = 2; // str; in
|
||||
const USERNAME_PARAM_KEY: u8 = 7; // str; in
|
||||
|
||||
pub(super) struct AddSanctionProvider;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl <C: Send + 'static> SimpleOperation<C> for AddSanctionProvider {
|
||||
type User = crate::UserTy;
|
||||
const CODE: u8 = CODE;
|
||||
|
||||
async fn handle(&self, params: ParameterTable<C>, user: &Self::User) -> Result<ParameterTable<C>, SimpleOpError> {
|
||||
let mut params = params.to_dict();
|
||||
if let Some(Typed::Int(sanction_ty)) = params.remove(&SANCTION_TY_PARAM_KEY) {
|
||||
if let Some(Typed::Bool(is_adding)) = params.remove(&IS_ADDING_PARAM_KEY) {
|
||||
if let Some(Typed::Int(duration)) = params.remove(&DURATION_PARAM_KEY) {
|
||||
if let Some(Typed::Str(reason)) = params.remove(&REASON_PARAM_KEY) {
|
||||
if let Some(Typed::Str(username)) = params.remove(&USERNAME_PARAM_KEY) {
|
||||
let user_info = user.user()?;
|
||||
let sanction = rc_core::persist::user::SetSanction {
|
||||
type_: rc_core::persist::user::SanctionType::from_i32(sanction_ty)?,
|
||||
is_adding,
|
||||
duration,
|
||||
reason: reason.string,
|
||||
username: username.string,
|
||||
};
|
||||
user_info.set_sanction(sanction).await?;
|
||||
return Ok(params.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err((rc_core::data::error_codes::ChatErrorCodes::UnexpectedError as i16).into())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn add_modify_sanction_provider<C: Send + 'static>() -> SimpleOpImpl<C, crate::UserTy, AddSanctionProvider> {
|
||||
SimpleOpImpl::new(AddSanctionProvider)
|
||||
}
|
||||
30
rc_chat_room/src/operations/list_sanctions.rs
Normal file
30
rc_chat_room/src/operations/list_sanctions.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||
use polariton::operation::{ParameterTable, Typed};
|
||||
|
||||
const CODE: u8 = 16;
|
||||
|
||||
const SANCTIONS_PARAM_KEY: u8 = 31; // arr of str; out
|
||||
const USERNAME_PARAM_KEY: u8 = 22; // str; in
|
||||
|
||||
pub(super) struct GetSanctionsProvider;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl SimpleOperation<()> for GetSanctionsProvider {
|
||||
type User = crate::UserTy;
|
||||
const CODE: u8 = CODE;
|
||||
|
||||
async fn handle(&self, params: ParameterTable, user: &Self::User) -> Result<ParameterTable, SimpleOpError> {
|
||||
let mut params = params.to_dict();
|
||||
if let Some(Typed::Str(username)) = params.remove(&USERNAME_PARAM_KEY) {
|
||||
let user_info = user.user()?;
|
||||
let sanctions = user_info.get_sanctions(username.string).await?;
|
||||
params.insert(SANCTIONS_PARAM_KEY, sanctions);
|
||||
return Ok(params.into());
|
||||
}
|
||||
Err((rc_core::data::error_codes::ChatErrorCodes::UnexpectedError as i16).into())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn list_sanctions_provider() -> SimpleOpImpl<(), crate::UserTy, GetSanctionsProvider> {
|
||||
SimpleOpImpl::new(GetSanctionsProvider)
|
||||
}
|
||||
@@ -7,6 +7,8 @@ mod public_channels;
|
||||
mod join_channel;
|
||||
mod user_online;
|
||||
mod subscribed_channels;
|
||||
mod add_modify_sanction;
|
||||
mod list_sanctions;
|
||||
|
||||
use polariton_server::operations::OperationsHandler;
|
||||
|
||||
@@ -25,5 +27,7 @@ pub fn handler(chat_system: crate::state::chat::ChatImpl, conf: &rc_core::persis
|
||||
.add(send_message::send_private_message_handler(chat_system.clone()))
|
||||
.add(join_channel::leave_channel_provider(chat_system.clone()))
|
||||
.add(subscribed_channels::all_subbed_channels_provider())
|
||||
.add(add_modify_sanction::add_modify_sanction_provider())
|
||||
.add(list_sanctions::list_sanctions_provider())
|
||||
//.add(polariton_server::operations::Ack::<00000, _>::default())
|
||||
}
|
||||
|
||||
@@ -1,12 +1,26 @@
|
||||
use polariton_server::operations::SimpleFunc;
|
||||
use polariton_server::operations::{SimpleOpError, SimpleOperation, SimpleOpImpl};
|
||||
use polariton::operation::{ParameterTable, Typed};
|
||||
|
||||
const CODE: u8 = 15;
|
||||
|
||||
const PARAM_KEY: u8 = 28;
|
||||
|
||||
pub(super) fn pending_sanctions_checker() -> SimpleFunc<15, crate::UserTy, impl (Fn(ParameterTable, &crate::UserTy) -> Result<ParameterTable, i16>) + Sync + Sync> {
|
||||
SimpleFunc::new(|params, _| {
|
||||
pub(super) struct PendingSanctionsProvider;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl <C: Send + 'static> SimpleOperation<C> for PendingSanctionsProvider {
|
||||
type User = crate::UserTy;
|
||||
const CODE: u8 = CODE;
|
||||
|
||||
async fn handle(&self, params: ParameterTable<C>, _user: &Self::User) -> Result<ParameterTable<C>, SimpleOpError> {
|
||||
let mut params = params.to_dict();
|
||||
params.insert(PARAM_KEY, Typed::Bool(false.into()));
|
||||
//let user_info = user.user()?;
|
||||
//params.insert(PARAM_KEY, Typed::Bool(user_info.has_pending_sanctions().await?));
|
||||
params.insert(PARAM_KEY, Typed::Bool(false));
|
||||
Ok(params.into())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn pending_sanctions_checker<C: Send + 'static>() -> SimpleOpImpl<C, crate::UserTy, PendingSanctionsProvider> {
|
||||
SimpleOpImpl::new(PendingSanctionsProvider)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ pub mod weapon_list;
|
||||
pub mod weapon_upgrade;
|
||||
pub mod crf;
|
||||
pub mod channel;
|
||||
pub mod sanction;
|
||||
|
||||
pub mod error_codes;
|
||||
|
||||
|
||||
72
rc_core/src/data/sanction.rs
Normal file
72
rc_core/src/data/sanction.rs
Normal file
@@ -0,0 +1,72 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct SanctionJson {
|
||||
#[serde(rename="Type")]
|
||||
pub type_: SanctionType,
|
||||
#[serde(rename="Reason")]
|
||||
pub reason: String,
|
||||
#[serde(rename="Reporter")]
|
||||
pub reporter: String,
|
||||
#[serde(rename="Issued", serialize_with="serde_issued::serialize")]
|
||||
pub issued: chrono::DateTime<chrono::Utc>,
|
||||
}
|
||||
|
||||
impl SanctionJson {
|
||||
pub fn as_json(&self) -> String {
|
||||
serde_json::to_string(&self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
mod serde_issued {
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
/*pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<DateTime<Utc>, D::Error> {
|
||||
d.deserialize_str(todo!())
|
||||
}*/
|
||||
|
||||
pub fn serialize<S: serde::Serializer>(dt: &DateTime<Utc>, serializer: S) -> Result<S::Ok, S::Error> {
|
||||
serializer.serialize_str(&dt.to_rfc2822())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug)]
|
||||
pub enum SanctionType {
|
||||
Warning = 0,
|
||||
Mute = 1,
|
||||
Suspension = 2,
|
||||
Note = 3,
|
||||
Kick = 4,
|
||||
}
|
||||
|
||||
impl SanctionType {
|
||||
pub(crate) fn from_db(t: rc_database::schema::sanction::Descriptor) -> Self {
|
||||
match t {
|
||||
rc_database::schema::sanction::Descriptor::Warn => Self::Warning,
|
||||
rc_database::schema::sanction::Descriptor::Mute => Self::Mute,
|
||||
rc_database::schema::sanction::Descriptor::Ban => Self::Suspension,
|
||||
rc_database::schema::sanction::Descriptor::Note => Self::Note,
|
||||
rc_database::schema::sanction::Descriptor::Kick => Self::Kick,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn to_db(self) -> rc_database::schema::sanction::Descriptor {
|
||||
match self {
|
||||
Self::Warning => rc_database::schema::sanction::Descriptor::Warn,
|
||||
Self::Mute => rc_database::schema::sanction::Descriptor::Mute,
|
||||
Self::Suspension => rc_database::schema::sanction::Descriptor::Ban,
|
||||
Self::Note => rc_database::schema::sanction::Descriptor::Note,
|
||||
Self::Kick => rc_database::schema::sanction::Descriptor::Kick,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_persist(t: crate::persist::user::SanctionType) -> Self {
|
||||
match t {
|
||||
crate::persist::user::SanctionType::Warn => Self::Warning,
|
||||
crate::persist::user::SanctionType::Mute => Self::Mute,
|
||||
crate::persist::user::SanctionType::Ban => Self::Suspension,
|
||||
crate::persist::user::SanctionType::Note => Self::Note,
|
||||
crate::persist::user::SanctionType::Kick => Self::Kick,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -198,6 +198,33 @@ impl UserData {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn check_perms_to_exec(&self, ty: &super::SanctionType) -> Result<(), i16> {
|
||||
match ty {
|
||||
super::SanctionType::Warn
|
||||
| super::SanctionType::Mute
|
||||
| super::SanctionType::Note => if self.has_any_elevated_perms() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(crate::data::error_codes::ChatErrorCodes::ModeratorsOnly as i16)
|
||||
},
|
||||
super::SanctionType::Ban
|
||||
| super::SanctionType::Kick => if self.has_admin_or_better_perms() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(crate::data::error_codes::ChatErrorCodes::AdminsOnly as i16)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_any_elevated_perms(&self) -> bool {
|
||||
self.perms.moderator | self.perms.administrator | self.perms.developer
|
||||
}
|
||||
|
||||
fn has_admin_or_better_perms(&self) -> bool {
|
||||
self.perms.administrator | self.perms.developer
|
||||
}
|
||||
}
|
||||
|
||||
const INVALID_ROBOT_ERR: i16 = crate::data::error_codes::WebServicesError::InvalidRobot as i16; // 140
|
||||
@@ -716,4 +743,83 @@ impl super::ChatUser for UserData {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*async fn has_pending_sanctions(&self) -> Result<bool, i16> {
|
||||
let count = self.db.count_sanctions_to_ack_by_user_id_and_descriptor(self.account.id, rc_database::schema::sanction::Descriptor::Warn).await.map_err(|e| {
|
||||
log::error!("Failed to count pending sanctions for user_id {}: {}", self.account.id, e);
|
||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||
})?;
|
||||
Ok(count != 0)
|
||||
}*/
|
||||
|
||||
async fn get_sanctions(&self, username: String) -> Result<polariton::operation::Typed<()>, i16> {
|
||||
let user_opt = self.db.user_by_display_name(username.clone()).await.map_err(|e| {
|
||||
log::error!("Failed to retrieve user by username {} for user_id {}: {}", username, self.account.id, e);
|
||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||
})?;
|
||||
if let Some(user) = user_opt {
|
||||
let sanctions = self.db.sanctions_by_user_id(user.id).await.map_err(|e| {
|
||||
log::error!("Failed to retrieve sanctions by username {} for user_id {}: {}", username, self.account.id, e);
|
||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||
})?;
|
||||
Ok(polariton::operation::Typed::Arr(polariton::operation::Arr {
|
||||
ty: polariton::serdes::TypePrefix::Str,
|
||||
items: sanctions.into_iter().map(|x| {
|
||||
let data = crate::data::sanction::SanctionJson {
|
||||
type_: crate::data::sanction::SanctionType::from_db(x.descriptor),
|
||||
reason: x.reason,
|
||||
reporter: x.issuer_name,
|
||||
issued: chrono::DateTime::from_timestamp(x.creation_time, 0).unwrap(),
|
||||
};
|
||||
polariton::operation::Typed::Str(data.as_json().into())
|
||||
}).collect(),
|
||||
}))
|
||||
} else {
|
||||
Err(crate::data::error_codes::ChatErrorCodes::DoesNotExist as i16)
|
||||
}
|
||||
}
|
||||
|
||||
async fn set_sanction(&self, sanction: super::SetSanction) -> Result<(), i16> {
|
||||
self.check_perms_to_exec(&sanction.type_)?;
|
||||
let user_opt = self.db.user_by_display_name(sanction.username.clone()).await.map_err(|e| {
|
||||
log::error!("Failed to retrieve user by username {} for user_id {}: {}", sanction.username, self.account.id, e);
|
||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||
})?;
|
||||
if let Some(user) = user_opt {
|
||||
if sanction.is_adding {
|
||||
let now = chrono::Utc::now().timestamp();
|
||||
let sanction_ty = crate::data::sanction::SanctionType::from_persist(sanction.type_).to_db();
|
||||
let to_add = rc_database::schema::sanction::ActiveModel {
|
||||
user_id: rc_database::sea_orm::ActiveValue::Set(user.id),
|
||||
creation_time: rc_database::sea_orm::ActiveValue::Set(now),
|
||||
issuer_id: rc_database::sea_orm::ActiveValue::Set(self.account.id),
|
||||
issuer_name: rc_database::sea_orm::ActiveValue::Set(self.account.display_name.clone()),
|
||||
descriptor: rc_database::sea_orm::ActiveValue::Set(sanction_ty.clone()),
|
||||
reason: rc_database::sea_orm::ActiveValue::Set(sanction.reason),
|
||||
duration: rc_database::sea_orm::ActiveValue::Set(if sanction.duration <= 0 { None } else { Some(sanction.duration as i64) }),
|
||||
..Default::default()
|
||||
};
|
||||
if matches!(sanction_ty, rc_database::schema::sanction::Descriptor::Ban) {
|
||||
self.db.update_perms_by_user_id(rc_database::schema::permissions::ActiveModel {
|
||||
banned: rc_database::sea_orm::ActiveValue::Set(true),
|
||||
..Default::default()
|
||||
}, user.id).await.map_err(|e| {
|
||||
log::error!("Failed to update permissions (to ban) for user_id {} by user_id {}: {}", user.id, self.account.id, e);
|
||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||
})?;
|
||||
}
|
||||
self.db.insert_sanction(to_add).await.map_err(|e| {
|
||||
log::error!("Failed to insert sanction for user_id {} by user_id {}: {}", user.id, self.account.id, e);
|
||||
crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16
|
||||
})?;
|
||||
Ok(())
|
||||
} else {
|
||||
// FIXME
|
||||
log::error!("Modifying sanctions is not currently supported");
|
||||
Err(crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16)
|
||||
}
|
||||
} else {
|
||||
Err(crate::data::error_codes::ChatErrorCodes::DoesNotExist as i16)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ mod inventory;
|
||||
pub use inventory::UnlockedParts;
|
||||
|
||||
mod traits;
|
||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData};
|
||||
pub use traits::{UserProvider, User, UserToken, UserSlots, UserSlotData, VehicleData, UserInfo, UserLoginInfo, ExtraUserInfo, UserAuthenticator, NewSlotData, UserId, RegistrationInfo, VehicleUploadData, ChatUser, AvatarInfo, GetAvatarInfo, ControlData, ControlType, CustomisationData, GetCustomisationData, SetSanction, SanctionType};
|
||||
|
||||
pub const TOKEN_SECRET_FILENAME: &str = "token_secret.key";
|
||||
|
||||
|
||||
@@ -190,5 +190,36 @@ pub trait ChatUser {
|
||||
async fn subscribed_channels_strings(&self) -> Result<Vec<String>, i16>;
|
||||
async fn add_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<polariton::operation::Typed<()>, i16>;
|
||||
async fn remove_subscribed_channel(&self, channel: String, channel_ty: crate::data::channel::ChatChannelType) -> Result<(), i16>;
|
||||
//async fn has_pending_sanctions(&self) -> Result<bool, i16>;
|
||||
async fn get_sanctions(&self, username: String) -> Result<polariton::operation::Typed<()>, i16>;
|
||||
async fn set_sanction(&self, sanction: SetSanction) -> Result<(), i16>;
|
||||
}
|
||||
|
||||
pub struct SetSanction {
|
||||
pub is_adding: bool, // if false, it's modifying
|
||||
pub type_: SanctionType,
|
||||
pub duration: i32,
|
||||
pub reason: String,
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
pub enum SanctionType {
|
||||
Warn = 0,
|
||||
Mute = 1,
|
||||
Ban = 2,
|
||||
Note = 3,
|
||||
Kick = 4,
|
||||
}
|
||||
|
||||
impl SanctionType {
|
||||
pub fn from_i32(i: i32) -> Result<Self, i16> {
|
||||
match i {
|
||||
0 => Ok(Self::Warn),
|
||||
1 => Ok(Self::Mute),
|
||||
2 => Ok(Self::Ban),
|
||||
3 => Ok(Self::Note),
|
||||
4 => Ok(Self::Kick),
|
||||
_ => Err(crate::data::error_codes::ChatErrorCodes::UnexpectedError as i16),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
pub struct Migration;
|
||||
|
||||
impl MigrationName for Migration {
|
||||
fn name(&self) -> &str {
|
||||
"m20250529_000001_create_sanction_table"
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl MigrationTrait for Migration {
|
||||
// Define how to apply this migration: Create the Sanctions table.
|
||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.create_table(
|
||||
Table::create()
|
||||
.table(crate::schema::sanction::Entity)
|
||||
.col(
|
||||
ColumnDef::new(crate::schema::sanction::Column::Id)
|
||||
.unsigned()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(ColumnDef::new(crate::schema::sanction::Column::UserId).unsigned().not_null())
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk-sanction-user_id")
|
||||
.from(crate::schema::sanction::Entity, crate::schema::sanction::Column::UserId)
|
||||
.to(crate::schema::user::Entity, crate::schema::user::Column::Id),
|
||||
)
|
||||
.col(ColumnDef::new(crate::schema::sanction::Column::CreationTime).big_integer().not_null())
|
||||
.col(ColumnDef::new(crate::schema::sanction::Column::IssuerId).unsigned().not_null())
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk-sanction-issuer_id")
|
||||
.from(crate::schema::sanction::Entity, crate::schema::sanction::Column::IssuerId)
|
||||
.to(crate::schema::user::Entity, crate::schema::user::Column::Id),
|
||||
)
|
||||
.col(ColumnDef::new(crate::schema::sanction::Column::IssuerName).string().not_null())
|
||||
.col(ColumnDef::new(crate::schema::sanction::Column::Descriptor).string().not_null())
|
||||
.col(ColumnDef::new(crate::schema::sanction::Column::Reason).string().not_null())
|
||||
.col(ColumnDef::new(crate::schema::sanction::Column::Duration).big_integer())
|
||||
.col(ColumnDef::new(crate::schema::sanction::Column::Acknowledged).big_integer())
|
||||
.col(ColumnDef::new(crate::schema::sanction::Column::AppealerId).unsigned())
|
||||
.foreign_key(
|
||||
ForeignKey::create()
|
||||
.name("fk-sanction-appealer_id")
|
||||
.from(crate::schema::sanction::Entity, crate::schema::sanction::Column::AppealerId)
|
||||
.to(crate::schema::user::Entity, crate::schema::user::Column::Id),
|
||||
)
|
||||
.col(ColumnDef::new(crate::schema::sanction::Column::AppealTime).big_integer())
|
||||
.to_owned(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
// Define how to rollback this migration: Drop the Sanctions table.
|
||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||
manager
|
||||
.drop_table(Table::drop().table(crate::schema::sanction::Entity).to_owned())
|
||||
.await
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ mod m20250424_000003_create_garage_table;
|
||||
mod m20250424_000004_create_user_aux_table;
|
||||
mod m20250424_000005_create_campaign_tables;
|
||||
mod m20250526_000001_add_garage_customisation;
|
||||
mod m20250529_000001_create_sanction_table;
|
||||
|
||||
pub struct Migrator;
|
||||
|
||||
@@ -19,6 +20,7 @@ impl MigratorTrait for Migrator {
|
||||
Box::new(m20250424_000004_create_user_aux_table::Migration),
|
||||
Box::new(m20250424_000005_create_campaign_tables::Migration),
|
||||
Box::new(m20250526_000001_add_garage_customisation::Migration),
|
||||
Box::new(m20250529_000001_create_sanction_table::Migration),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ pub mod garage;
|
||||
pub mod campaign;
|
||||
pub mod campaign_difficulty_completion;
|
||||
pub mod common_query;
|
||||
pub mod sanction;
|
||||
|
||||
pub fn parse_int_csv(s: &str) -> Vec<u32> {
|
||||
s.split(',').filter_map(|i_as_s| {
|
||||
|
||||
46
rc_database/src/schema/sanction.rs
Normal file
46
rc_database/src/schema/sanction.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "sanctions")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: u32,
|
||||
pub user_id: u32,
|
||||
pub creation_time: i64, // seconds since unix epoch
|
||||
pub issuer_id: u32,
|
||||
pub issuer_name: String,
|
||||
pub descriptor: Descriptor,
|
||||
pub reason: String,
|
||||
pub duration: Option<i64>, // seconds after creation_time (null means permanent or irrelevant)
|
||||
pub acknowledged: Option<i64>, // seconds since unix epoch
|
||||
pub appealer_id: Option<u32>, // moderator who approved appeal
|
||||
pub appeal_time: Option<i64>, // seconds since unix epoch
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {
|
||||
#[sea_orm(
|
||||
belongs_to = "super::user::Entity",
|
||||
from = "Column::UserId",
|
||||
to = "super::user::Column::Id"
|
||||
)]
|
||||
User,
|
||||
}
|
||||
|
||||
impl Related<super::user::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
Relation::User.def()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
|
||||
#[sea_orm(rs_type = "String", db_type = "String(StringLen::None)", rename_all = "PascalCase")]
|
||||
pub enum Descriptor {
|
||||
Warn,
|
||||
Mute,
|
||||
Ban,
|
||||
Note,
|
||||
Kick,
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use sea_orm_migration::MigratorTrait;
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect, TransactionTrait};
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect, TransactionTrait};
|
||||
|
||||
pub struct Database {
|
||||
orm: sea_orm::DatabaseConnection,
|
||||
@@ -105,6 +105,24 @@ impl Database {
|
||||
entity.insert(&self.orm).await
|
||||
}
|
||||
|
||||
pub async fn update_perms_by_user_id(&self, mut entity: crate::schema::permissions::ActiveModel, user_id: u32) -> Result<Option<crate::schema::permissions::Model>, sea_orm::DbErr> {
|
||||
let id_opt = crate::schema::permissions::Entity::find()
|
||||
.select_only()
|
||||
.column(crate::schema::permissions::Column::Id)
|
||||
.filter(crate::schema::permissions::Column::UserId.eq(user_id))
|
||||
.into_model::<crate::schema::common_query::Id>()
|
||||
.one(&self.orm)
|
||||
.await?;
|
||||
if let Some(id) = id_opt {
|
||||
entity.id = sea_orm::ActiveValue::Set(id.id);
|
||||
Ok(Some(crate::schema::permissions::Entity::update(entity)
|
||||
.exec(&self.orm)
|
||||
.await?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn garage_max_slot_by_user_id(&self, user_id: u32) -> Result<u32, sea_orm::DbErr> {
|
||||
let result = crate::schema::garage::Entity::find()
|
||||
.select_only()
|
||||
@@ -227,4 +245,26 @@ impl Database {
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn count_sanctions_to_ack_by_user_id_and_descriptor(&self, user_id: u32, desc: crate::schema::sanction::Descriptor) -> Result<u64, sea_orm::DbErr> {
|
||||
crate::schema::sanction::Entity::find()
|
||||
.filter(crate::schema::sanction::Column::UserId.eq(user_id))
|
||||
.filter(crate::schema::sanction::Column::Descriptor.eq(desc))
|
||||
.filter(crate::schema::sanction::Column::Acknowledged.is_null())
|
||||
.count(&self.orm)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn sanctions_by_user_id(&self, user_id: u32) -> Result<Vec<crate::schema::sanction::Model>, sea_orm::DbErr> {
|
||||
crate::schema::sanction::Entity::find()
|
||||
.filter(crate::schema::sanction::Column::UserId.eq(user_id))
|
||||
.filter(crate::schema::sanction::Column::Acknowledged.is_null())
|
||||
.order_by_asc(crate::schema::sanction::Column::CreationTime)
|
||||
.all(&self.orm)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn insert_sanction(&self, entity: crate::schema::sanction::ActiveModel) -> Result<crate::schema::sanction::Model, sea_orm::DbErr> {
|
||||
entity.insert(&self.orm).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +164,7 @@ pub fn handler(init_ctx: &crate::InitConfig) -> OperationsHandler<crate::UserTy>
|
||||
.add(polariton_server::operations::Ack::<152, _>::default()) // custom game player state changed (188 is desired state)
|
||||
.add(custom_games_invite::pending_invite_provider())
|
||||
.add(chat_settings::chat_settings_provider())
|
||||
.add(polariton_server::operations::Ack::<19, _>::default()) // save chat settings
|
||||
.add(prebuilt_robots::garage_robot_data_provider())
|
||||
.add(prebuilt_colours::garage_colour_combo_provider())
|
||||
.add(robopass_preview_items::robopass_preview_provider())
|
||||
|
||||
Reference in New Issue
Block a user