mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
fix(admin): save premium limits and model changes (#3386)
This commit is contained in:
1 parent
7d2c51a57f
commit
6acd7f3d22
18 files changed
+1035
-59
No files matched your search
@@ -143,7 +143,7 @@ pub(super) fn billing_result<T>(result: Result<T, ApiError>) -> FlashData {
|
||||
}
|
||||
}
|
||||
|
||||
fn validation_message(error: &ApiError) -> Option<String> {
|
||||
pub(super) fn validation_message(error: &ApiError) -> Option<String> {
|
||||
let ApiError::Http {
|
||||
status: 400,
|
||||
message,
|
||||
|
||||
@@ -18,7 +18,7 @@ use crate::{
|
||||
InstanceIntegrationsUpdateRequest, InstanceMediaUpdateRequest,
|
||||
InstancePolicyUpdateRequest, InstanceRegistrationConfigUpdateRequest,
|
||||
InstanceServicesUpdateRequest, InstanceYoutubeIntegrationUpdateRequest,
|
||||
LimitConfigUpdateRequest, LimitRule, LimitRuleFilters, PremiumMode,
|
||||
LimitConfigUpdateRequest, LimitKeyMetadata, LimitRule, LimitRuleFilters, PremiumMode,
|
||||
PushRelayConfigUpdateRequest, RegistrationMode, SsoConfigUpdateRequest,
|
||||
VOICE_P2P_MAX_PARTICIPANTS_RANGE, VoiceE2eeScope, VoiceP2pConfigUpdateRequest,
|
||||
},
|
||||
@@ -405,12 +405,17 @@ fn render_fragment_with_toast(markup: Markup, flash: &FlashData) -> Response {
|
||||
response
|
||||
}
|
||||
|
||||
fn instance_config_result<T, E: std::fmt::Display>(result: Result<T, E>) -> FlashData {
|
||||
fn instance_config_result<T>(result: Result<T, crate::api::client::ApiError>) -> FlashData {
|
||||
match result {
|
||||
Ok(_) => FlashData::success("Instance config updated"),
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "admin API request failed: update instance config");
|
||||
FlashData::error("Failed to update instance config")
|
||||
match super::billing_actions::validation_message(&error) {
|
||||
Some(message) => {
|
||||
FlashData::error(format!("Failed to update instance config: {message}"))
|
||||
}
|
||||
None => FlashData::error("Failed to update instance config"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1042,6 +1047,7 @@ fn update_limit_rule_values(
|
||||
rule: &mut LimitRule,
|
||||
form: &MultiValueForm,
|
||||
limit_keys: &[String],
|
||||
metadata: &std::collections::BTreeMap<String, LimitKeyMetadata>,
|
||||
fallback_limits: Option<&std::collections::BTreeMap<String, u64>>,
|
||||
) {
|
||||
let mut limits = std::collections::BTreeMap::new();
|
||||
@@ -1055,12 +1061,19 @@ fn update_limit_rule_values(
|
||||
{
|
||||
limits.extend(defaults.clone());
|
||||
}
|
||||
for key in limit_keys {
|
||||
if metadata.get(key).is_some_and(|metadata| metadata.is_toggle)
|
||||
&& rule.limits.contains_key(key)
|
||||
{
|
||||
limits.entry(key.clone()).or_insert(0);
|
||||
}
|
||||
}
|
||||
rule.limits = limits;
|
||||
rule.filters = build_limit_filters(form);
|
||||
}
|
||||
|
||||
fn limit_config_result<T, E: std::fmt::Display>(
|
||||
result: Result<T, E>,
|
||||
fn limit_config_result<T>(
|
||||
result: Result<T, crate::api::client::ApiError>,
|
||||
success_message: &'static str,
|
||||
error_message: &'static str,
|
||||
) -> FlashData {
|
||||
@@ -1068,7 +1081,10 @@ fn limit_config_result<T, E: std::fmt::Display>(
|
||||
Ok(_) => FlashData::success(success_message),
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "admin API request failed: update limit config");
|
||||
FlashData::error(error_message)
|
||||
match super::billing_actions::validation_message(&error) {
|
||||
Some(message) => FlashData::error(format!("{error_message}: {message}")),
|
||||
None => FlashData::error(error_message),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1137,7 +1153,13 @@ pub async fn limit_config_post(
|
||||
.defaults
|
||||
.get(&rule_id)
|
||||
.or_else(|| current.defaults.get("default"));
|
||||
update_limit_rule_values(rule, &form, ¤t.limit_keys, fallback);
|
||||
update_limit_rule_values(
|
||||
rule,
|
||||
&form,
|
||||
¤t.limit_keys,
|
||||
¤t.metadata,
|
||||
fallback,
|
||||
);
|
||||
let request = LimitConfigUpdateRequest { limit_config };
|
||||
let result = client.update_limit_config(&request).await;
|
||||
let flash = limit_config_result(
|
||||
@@ -1147,6 +1169,43 @@ pub async fn limit_config_post(
|
||||
);
|
||||
return redirect_back_with_flash(base, "/limit-config", flash, secure_cookies);
|
||||
}
|
||||
"reset" => {
|
||||
let rule = aq
|
||||
.rule
|
||||
.as_deref()
|
||||
.and_then(clean_string)
|
||||
.and_then(|rule_id| {
|
||||
limit_config
|
||||
.rules
|
||||
.iter_mut()
|
||||
.find(|rule| rule.id == rule_id)
|
||||
});
|
||||
let Some(rule) = rule else {
|
||||
return redirect_back_with_flash(
|
||||
base,
|
||||
"/limit-config",
|
||||
FlashData::error("Rule not found"),
|
||||
secure_cookies,
|
||||
);
|
||||
};
|
||||
let Some(defaults) = current.defaults.get(&rule.id) else {
|
||||
return redirect_back_with_flash(
|
||||
base,
|
||||
"/limit-config",
|
||||
FlashData::error("This rule has no defaults to reset to"),
|
||||
secure_cookies,
|
||||
);
|
||||
};
|
||||
rule.limits = defaults.clone();
|
||||
let request = LimitConfigUpdateRequest { limit_config };
|
||||
let result = client.update_limit_config(&request).await;
|
||||
let flash = limit_config_result(
|
||||
result,
|
||||
"Limit rule reset to defaults",
|
||||
"Failed to reset limit rule",
|
||||
);
|
||||
return redirect_back_with_flash(base, "/limit-config", flash, secure_cookies);
|
||||
}
|
||||
"delete" => {
|
||||
let rule_id = match aq.rule.as_deref().and_then(clean_string) {
|
||||
Some(rule_id) => rule_id,
|
||||
@@ -1360,6 +1419,109 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn instance_config_result_surfaces_the_api_validation_message() {
|
||||
let error = crate::api::client::ApiError::Http {
|
||||
status: 400,
|
||||
message: serde_json::json!({
|
||||
"code": "INVALID_FORM_BODY",
|
||||
"message": "Input Validation Error",
|
||||
"errors": [{
|
||||
"path": "policy.premium_mode",
|
||||
"code": "X",
|
||||
"message": "Disable billing before switching the premium mode to everyone"
|
||||
}]
|
||||
})
|
||||
.to_string(),
|
||||
};
|
||||
assert_eq!(
|
||||
instance_config_result::<()>(Err(error)).message,
|
||||
"Failed to update instance config: policy.premium_mode: Disable billing before switching the premium mode to everyone"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limit_config_result_surfaces_the_api_validation_message() {
|
||||
let error = crate::api::client::ApiError::Http {
|
||||
status: 400,
|
||||
message: serde_json::json!({
|
||||
"code": "INVALID_FORM_BODY",
|
||||
"message": "Input Validation Error",
|
||||
"errors": [{
|
||||
"path": "limit_config.rules.0.limits.max_bio_length",
|
||||
"code": "VALUE_MUST_BE_INTEGER_IN_RANGE",
|
||||
"message": "Value must be an integer between 0 and 320."
|
||||
}]
|
||||
})
|
||||
.to_string(),
|
||||
};
|
||||
assert_eq!(
|
||||
limit_config_result::<()>(Err(error), "Saved", "Failed to update limit configuration")
|
||||
.message,
|
||||
"Failed to update limit configuration: limit_config.rules.0.limits.max_bio_length: Value must be an integer between 0 and 320."
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_limit_rule_values_saves_unchecked_toggles_as_off() {
|
||||
let toggle = |key: &str, is_toggle: bool| {
|
||||
(
|
||||
key.to_owned(),
|
||||
LimitKeyMetadata {
|
||||
key: key.to_owned(),
|
||||
label: key.to_owned(),
|
||||
description: String::new(),
|
||||
category: "features".to_owned(),
|
||||
scope: "user".to_owned(),
|
||||
is_toggle,
|
||||
unit: None,
|
||||
min: None,
|
||||
max: None,
|
||||
},
|
||||
)
|
||||
};
|
||||
let metadata = std::collections::BTreeMap::from([
|
||||
toggle("feature_animated_avatar", true),
|
||||
toggle("feature_animated_banner", true),
|
||||
toggle("max_guilds", false),
|
||||
]);
|
||||
let limit_keys = metadata.keys().cloned().collect::<Vec<_>>();
|
||||
let mut rule = LimitRule {
|
||||
id: "premium".to_owned(),
|
||||
filters: None,
|
||||
limits: std::collections::BTreeMap::from([
|
||||
("feature_animated_avatar".to_owned(), 1),
|
||||
("feature_animated_banner".to_owned(), 1),
|
||||
("max_guilds".to_owned(), 200),
|
||||
]),
|
||||
modified_fields: None,
|
||||
};
|
||||
let form =
|
||||
MultiValueForm::parse(b"traits=premium&feature_animated_banner=1&max_guilds=250");
|
||||
update_limit_rule_values(&mut rule, &form, &limit_keys, &metadata, None);
|
||||
assert_eq!(
|
||||
rule.limits,
|
||||
std::collections::BTreeMap::from([
|
||||
("feature_animated_avatar".to_owned(), 0),
|
||||
("feature_animated_banner".to_owned(), 1),
|
||||
("max_guilds".to_owned(), 250),
|
||||
])
|
||||
);
|
||||
|
||||
let mut custom = LimitRule {
|
||||
id: "vip".to_owned(),
|
||||
filters: None,
|
||||
limits: std::collections::BTreeMap::from([("max_guilds".to_owned(), 300)]),
|
||||
modified_fields: None,
|
||||
};
|
||||
let form = MultiValueForm::parse(b"traits=vip&max_guilds=300");
|
||||
update_limit_rule_values(&mut custom, &form, &limit_keys, &metadata, None);
|
||||
assert_eq!(
|
||||
custom.limits,
|
||||
std::collections::BTreeMap::from([("max_guilds".to_owned(), 300)])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_experiment_user_ids_splits_newlines_and_commas() {
|
||||
assert_eq!(
|
||||
@@ -1823,7 +1985,13 @@ mod tests {
|
||||
limits: std::collections::BTreeMap::new(),
|
||||
modified_fields: None,
|
||||
};
|
||||
update_limit_rule_values(&mut rule, &form, &["message_send".to_owned()], None);
|
||||
update_limit_rule_values(
|
||||
&mut rule,
|
||||
&form,
|
||||
&["message_send".to_owned()],
|
||||
&std::collections::BTreeMap::new(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(rule.limits.get("message_send"), Some(&1));
|
||||
assert_eq!(
|
||||
rule.filters.expect("filters").traits,
|
||||
|
||||
Reference in new issue
Block a user