feat: Allow restricting server creation to specific users (#685)

Signed-off-by: IAmTomahawkx <iamtomahawkx@gmail.com>
This commit is contained in:
Tom
2026-03-23 12:25:33 -07:00
committed by GitHub
parent cf2cedcee1
commit edfa97db10
7 changed files with 56 additions and 19 deletions

View File

@@ -220,6 +220,10 @@ new_user_hours = 72
# (should be greater than any one file upload limit) # (should be greater than any one file upload limit)
body_limit_size = 20_000_000 body_limit_size = 20_000_000
# If any userids are entered here, only those users will be able to create servers.
# Leave empty to allow all users to create servers
restrict_server_creation = []
[features.limits.new_user] [features.limits.new_user]
# Limits imposed on new users # Limits imposed on new users

View File

@@ -344,6 +344,8 @@ pub struct GlobalLimits {
pub new_user_hours: usize, pub new_user_hours: usize,
pub body_limit_size: usize, pub body_limit_size: usize,
pub restrict_server_creation: Vec<String>,
} }
#[derive(Deserialize, Debug, Clone)] #[derive(Deserialize, Debug, Clone)]

View File

@@ -37,6 +37,7 @@ impl IntoResponse for Error {
ErrorType::AlreadyPinned => StatusCode::BAD_REQUEST, ErrorType::AlreadyPinned => StatusCode::BAD_REQUEST,
ErrorType::NotPinned => StatusCode::BAD_REQUEST, ErrorType::NotPinned => StatusCode::BAD_REQUEST,
ErrorType::CantCreateServers => StatusCode::FORBIDDEN,
ErrorType::UnknownServer => StatusCode::NOT_FOUND, ErrorType::UnknownServer => StatusCode::NOT_FOUND,
ErrorType::InvalidRole => StatusCode::NOT_FOUND, ErrorType::InvalidRole => StatusCode::NOT_FOUND,
ErrorType::Banned => StatusCode::FORBIDDEN, ErrorType::Banned => StatusCode::FORBIDDEN,

View File

@@ -1,5 +1,5 @@
use std::panic::Location;
use std::fmt::Display; use std::fmt::Display;
use std::panic::Location;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
#[macro_use] #[macro_use]
@@ -104,6 +104,7 @@ pub enum ErrorType {
NotPinned, NotPinned,
// ? Server related errors // ? Server related errors
CantCreateServers,
UnknownServer, UnknownServer,
InvalidRole, InvalidRole,
Banned, Banned,
@@ -232,15 +233,14 @@ impl<T, E: std::fmt::Debug + std::error::Error> ToRevoltError<T> for Result<T, E
fn to_internal_error(self) -> Result<T, Error> { fn to_internal_error(self) -> Result<T, Error> {
let loc = Location::caller(); let loc = Location::caller();
self self.map_err(|e| {
.map_err(|e| {
log::error!("{e:?}"); log::error!("{e:?}");
#[cfg(feature = "sentry")] #[cfg(feature = "sentry")]
sentry::capture_error(&e); sentry::capture_error(&e);
Error { Error {
error_type: ErrorType::InternalError, error_type: ErrorType::InternalError,
location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()) location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()),
} }
}) })
} }
@@ -251,11 +251,9 @@ impl<T> ToRevoltError<T> for Option<T> {
fn to_internal_error(self) -> Result<T, Error> { fn to_internal_error(self) -> Result<T, Error> {
let loc = Location::caller(); let loc = Location::caller();
self.ok_or_else(|| { self.ok_or_else(|| Error {
Error {
error_type: ErrorType::InternalError, error_type: ErrorType::InternalError,
location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()) location: format!("{}:{}:{}", loc.file(), loc.line(), loc.column()),
}
}) })
} }
} }

View File

@@ -44,6 +44,7 @@ impl<'r> Responder<'r, 'static> for Error {
ErrorType::NotPinned => Status::BadRequest, ErrorType::NotPinned => Status::BadRequest,
ErrorType::InvalidFlagValue => Status::BadRequest, ErrorType::InvalidFlagValue => Status::BadRequest,
ErrorType::CantCreateServers => Status::Forbidden,
ErrorType::UnknownServer => Status::NotFound, ErrorType::UnknownServer => Status::NotFound,
ErrorType::InvalidRole => Status::NotFound, ErrorType::InvalidRole => Status::NotFound,
ErrorType::Banned => Status::Forbidden, ErrorType::Banned => Status::Forbidden,

View File

@@ -1,8 +1,8 @@
use std::collections::HashMap;
use revolt_config::config; use revolt_config::config;
use revolt_result::Result; use revolt_result::Result;
use rocket::serde::json::Json; use rocket::serde::json::Json;
use serde::Serialize; use serde::Serialize;
use std::collections::HashMap;
/// # hCaptcha Configuration /// # hCaptcha Configuration
#[derive(Serialize, JsonSchema, Debug)] #[derive(Serialize, JsonSchema, Debug)]
@@ -88,6 +88,10 @@ pub struct GlobalLimits {
/// max server channels /// max server channels
server_channels: i64, server_channels: i64,
body_limit_size: i64, body_limit_size: i64,
/// restrict server creation to these users.
/// if blank, all users can create servers
pub restrict_server_creation: Vec<String>,
} }
/// # User Limits /// # User Limits
@@ -125,7 +129,10 @@ impl UserLimits {
voice_quality: fl.voice_quality as i64, voice_quality: fl.voice_quality as i64,
video: fl.video, video: fl.video,
video_resolution: [fl.video_resolution[0] as i64, fl.video_resolution[1] as i64], video_resolution: [fl.video_resolution[0] as i64, fl.video_resolution[1] as i64],
video_aspect_ratio: [fl.video_aspect_ratio[0] as f64, fl.video_aspect_ratio[1] as f64], video_aspect_ratio: [
fl.video_aspect_ratio[0] as f64,
fl.video_aspect_ratio[1] as f64,
],
file_upload_size_limits: fl.file_upload_size_limit, file_upload_size_limits: fl.file_upload_size_limit,
} }
} }
@@ -219,10 +226,15 @@ pub async fn root() -> Result<Json<RevoltConfig>> {
server_roles: config.features.limits.global.server_roles as i64, server_roles: config.features.limits.global.server_roles as i64,
server_channels: config.features.limits.global.server_channels as i64, server_channels: config.features.limits.global.server_channels as i64,
body_limit_size: config.features.limits.global.body_limit_size as i64, body_limit_size: config.features.limits.global.body_limit_size as i64,
restrict_server_creation: config
.features
.limits
.global
.restrict_server_creation,
}, },
new_user: UserLimits::from_feature_limits(config.features.limits.new_user), new_user: UserLimits::from_feature_limits(config.features.limits.new_user),
default: UserLimits::from_feature_limits(config.features.limits.default), default: UserLimits::from_feature_limits(config.features.limits.default),
} },
}, },
ws: config.hosts.events, ws: config.hosts.events,
app: config.hosts.app, app: config.hosts.app,

View File

@@ -1,3 +1,4 @@
use revolt_config::config;
use revolt_database::{Database, Member, Server, User}; use revolt_database::{Database, Member, Server, User};
use revolt_models::v0; use revolt_models::v0;
use revolt_result::{create_error, Result}; use revolt_result::{create_error, Result};
@@ -20,6 +21,24 @@ pub async fn create_server(
return Err(create_error!(IsBot)); return Err(create_error!(IsBot));
} }
let config = config().await;
if !config
.features
.limits
.global
.restrict_server_creation
.is_empty()
&& !config
.features
.limits
.global
.restrict_server_creation
.contains(&user.id)
{
return Err(create_error!(CantCreateServers));
}
let data = data.into_inner(); let data = data.into_inner();
data.validate().map_err(|error| { data.validate().map_err(|error| {
create_error!(FailedValidation { create_error!(FailedValidation {