mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
feat(voice): soft connection limits for voice servers (#2694)
This commit is contained in:
1 parent
cadca2c18e
commit
1b1d48b05e
24 files changed
+654
-28
No files matched your search
@@ -4,7 +4,7 @@ use crate::{
|
||||
api::client::AdminApiClient,
|
||||
middleware::{auth::AuthContext, csrf},
|
||||
state::AppState,
|
||||
templates,
|
||||
templates::{self, pages::voice_servers::VoiceServersPageParams},
|
||||
};
|
||||
use axum::{
|
||||
Router,
|
||||
@@ -13,12 +13,34 @@ use axum::{
|
||||
routing::get,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct VoiceServersQuery {
|
||||
region_id: Option<String>,
|
||||
}
|
||||
|
||||
async fn load_server_connection_counts(client: &AdminApiClient) -> HashMap<String, i64> {
|
||||
let response = match client.get_gateway_voice_state_counts().await {
|
||||
Ok(response) => response,
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "admin API request failed: load voice state counts");
|
||||
return HashMap::new();
|
||||
}
|
||||
};
|
||||
let Some(servers) = response.data.get("servers").and_then(|v| v.as_array()) else {
|
||||
return HashMap::new();
|
||||
};
|
||||
servers
|
||||
.iter()
|
||||
.filter_map(|entry| {
|
||||
let server_id = entry.get("server_id")?.as_str()?.to_owned();
|
||||
let count = entry.get("voice_state_count")?.as_i64()?;
|
||||
Some((server_id, count))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route(
|
||||
@@ -79,17 +101,21 @@ async fn voice_servers_page(
|
||||
let markup = templates::pages::voice_servers::voice_servers_page(
|
||||
config,
|
||||
&auth.0,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
&csrf_token,
|
||||
&VoiceServersPageParams {
|
||||
region_id: None,
|
||||
region_name: None,
|
||||
servers: None,
|
||||
connection_counts: &HashMap::new(),
|
||||
error: None,
|
||||
csrf_token: &csrf_token,
|
||||
},
|
||||
);
|
||||
return Html(markup.into_string()).into_response();
|
||||
}
|
||||
};
|
||||
|
||||
let client = AdminApiClient::new(state.http_client(), config, &auth.0.session);
|
||||
let connection_counts = load_server_connection_counts(&client).await;
|
||||
|
||||
let region_name = match client.get_voice_region(region_id, false).await {
|
||||
Ok(resp) => resp
|
||||
@@ -107,11 +133,14 @@ async fn voice_servers_page(
|
||||
let markup = templates::pages::voice_servers::voice_servers_page(
|
||||
config,
|
||||
&auth.0,
|
||||
Some(region_id),
|
||||
Some(®ion_name),
|
||||
Some(&resp.servers),
|
||||
None,
|
||||
&csrf_token,
|
||||
&VoiceServersPageParams {
|
||||
region_id: Some(region_id),
|
||||
region_name: Some(®ion_name),
|
||||
servers: Some(&resp.servers),
|
||||
connection_counts: &connection_counts,
|
||||
error: None,
|
||||
csrf_token: &csrf_token,
|
||||
},
|
||||
);
|
||||
Html(markup.into_string()).into_response()
|
||||
}
|
||||
@@ -120,11 +149,14 @@ async fn voice_servers_page(
|
||||
let markup = templates::pages::voice_servers::voice_servers_page(
|
||||
config,
|
||||
&auth.0,
|
||||
Some(region_id),
|
||||
Some(®ion_name),
|
||||
None,
|
||||
Some(&msg),
|
||||
&csrf_token,
|
||||
&VoiceServersPageParams {
|
||||
region_id: Some(region_id),
|
||||
region_name: Some(®ion_name),
|
||||
servers: None,
|
||||
connection_counts: &connection_counts,
|
||||
error: Some(&msg),
|
||||
csrf_token: &csrf_token,
|
||||
},
|
||||
);
|
||||
Html(markup.into_string()).into_response()
|
||||
}
|
||||
|
||||
@@ -92,6 +92,16 @@ pub(crate) fn build_server_body(form: &MultiValueForm) -> serde_json::Value {
|
||||
body.insert("longitude".into(), lng.into());
|
||||
}
|
||||
body.insert("is_active".into(), form.bool_value("is_active").into());
|
||||
if let Some(raw) = form.first("soft_connection_limit") {
|
||||
let trimmed = raw.trim();
|
||||
if trimmed.is_empty() {
|
||||
body.insert("soft_connection_limit".into(), serde_json::Value::Null);
|
||||
} else if let Ok(limit) = trimmed.parse::<i64>()
|
||||
&& limit > 0
|
||||
{
|
||||
body.insert("soft_connection_limit".into(), limit.into());
|
||||
}
|
||||
}
|
||||
body.insert("vip_only".into(), form.bool_value("vip_only").into());
|
||||
body.insert(
|
||||
"required_guild_features".into(),
|
||||
@@ -255,6 +265,47 @@ mod tests {
|
||||
assert_eq!(body["allowed_guild_ids"], serde_json::json!(["1", "2"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_server_body_sets_soft_connection_limit_from_a_positive_value() {
|
||||
let form =
|
||||
MultiValueForm::parse(b"region_id=us-east&server_id=s1&soft_connection_limit=250");
|
||||
let body = build_server_body(&form);
|
||||
assert_eq!(body["soft_connection_limit"], serde_json::json!(250));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_server_body_clears_soft_connection_limit_when_the_field_is_empty() {
|
||||
let form = MultiValueForm::parse(b"region_id=us-east&server_id=s1&soft_connection_limit=");
|
||||
let body = build_server_body(&form);
|
||||
assert_eq!(body["soft_connection_limit"], serde_json::Value::Null);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_server_body_omits_soft_connection_limit_when_the_field_is_absent_or_invalid() {
|
||||
let absent = MultiValueForm::parse(b"region_id=us-east&server_id=s1&is_active=true");
|
||||
assert!(
|
||||
!build_server_body(&absent)
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.contains_key("soft_connection_limit")
|
||||
);
|
||||
let invalid =
|
||||
MultiValueForm::parse(b"region_id=us-east&server_id=s1&soft_connection_limit=abc");
|
||||
assert!(
|
||||
!build_server_body(&invalid)
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.contains_key("soft_connection_limit")
|
||||
);
|
||||
let zero = MultiValueForm::parse(b"region_id=us-east&server_id=s1&soft_connection_limit=0");
|
||||
assert!(
|
||||
!build_server_body(&zero)
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.contains_key("soft_connection_limit")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_server_body_preserves_single_and_repeated_values() {
|
||||
let form = MultiValueForm::parse(
|
||||
|
||||
Reference in new issue
Block a user