mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
refactor(api): emit moderation events and apply account actions (#3031)
This commit is contained in:
1 parent
c488906131
commit
364084c819
385 files changed
+8994
-22221
No files matched your search
@@ -23,10 +23,6 @@ pub fn router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/ip-bans", get(ip_bans).post(ip_bans_post))
|
||||
.route("/email-bans", get(email_bans).post(email_bans_post))
|
||||
.route(
|
||||
"/suspicious-email-domains",
|
||||
get(suspicious_email_domains).post(suspicious_email_domains_post),
|
||||
)
|
||||
.route("/phrase-bans", get(phrase_bans).post(phrase_bans_post))
|
||||
.route("/url-bans", get(url_bans).post(url_bans_post))
|
||||
.route(
|
||||
@@ -72,7 +68,6 @@ macro_rules! ban_get {
|
||||
|
||||
ban_get!(ip_bans, "ip-bans");
|
||||
ban_get!(email_bans, "email-bans");
|
||||
ban_get!(suspicious_email_domains, "suspicious-email-domains");
|
||||
ban_get!(phrase_bans, "phrase-bans");
|
||||
ban_get!(url_bans, "url-bans");
|
||||
ban_get!(file_sha_bans, "file-sha-bans");
|
||||
@@ -141,7 +136,6 @@ macro_rules! ban_post {
|
||||
|
||||
ban_post!(ip_bans_post, "ip-bans");
|
||||
ban_post!(email_bans_post, "email-bans");
|
||||
ban_post!(suspicious_email_domains_post, "suspicious-email-domains");
|
||||
ban_post!(phrase_bans_post, "phrase-bans");
|
||||
ban_post!(url_bans_post, "url-bans");
|
||||
ban_post!(file_sha_bans_post, "file-sha-bans");
|
||||
|
||||
@@ -116,11 +116,6 @@ async fn execute_single_ban(
|
||||
let result = match ban_type {
|
||||
"ip-bans" => client.ban_ip(value, audit_log_reason).await,
|
||||
"email-bans" => client.ban_email(value, audit_log_reason).await,
|
||||
"suspicious-email-domains" => {
|
||||
client
|
||||
.add_suspicious_email_domain(value, audit_log_reason)
|
||||
.await
|
||||
}
|
||||
"phrase-bans" => client.ban_phrase(value, audit_log_reason).await,
|
||||
"url-bans" => client.ban_url(value, audit_log_reason).await,
|
||||
"file-sha-bans" => client.ban_file_sha(value, audit_log_reason).await,
|
||||
@@ -143,11 +138,6 @@ async fn execute_single_unban(
|
||||
let result = match ban_type {
|
||||
"ip-bans" => client.unban_ip(value, audit_log_reason).await,
|
||||
"email-bans" => client.unban_email(value, audit_log_reason).await,
|
||||
"suspicious-email-domains" => {
|
||||
client
|
||||
.remove_suspicious_email_domain(value, audit_log_reason)
|
||||
.await
|
||||
}
|
||||
"phrase-bans" => client.unban_phrase(value, audit_log_reason).await,
|
||||
"url-bans" => client.unban_url(value, audit_log_reason).await,
|
||||
"file-sha-bans" => client.unban_file_sha(value, audit_log_reason).await,
|
||||
@@ -169,7 +159,6 @@ async fn execute_check(
|
||||
let result = match ban_type {
|
||||
"ip-bans" => client.check_ip_ban(value).await,
|
||||
"email-bans" => client.check_email_ban(value).await,
|
||||
"suspicious-email-domains" => client.check_suspicious_email_domain(value).await,
|
||||
"phrase-bans" => client.check_phrase_ban(value).await,
|
||||
"url-bans" => client.check_url_ban(value).await,
|
||||
"file-sha-bans" => client.check_file_sha_ban(value).await,
|
||||
|
||||
@@ -8,20 +8,19 @@ use crate::{
|
||||
AltchaCaptchaConfigUpdateRequest, AppBrandingConfigUpdateRequest,
|
||||
AppLegalConfigUpdateRequest, AppPublicConfigUpdateRequest,
|
||||
AppRegistrationConfigUpdateRequest, AppSetupConfigUpdateRequest,
|
||||
CreateRegistrationUrlRequest, DeferredPhoneGateUpdateRequest,
|
||||
DomainMigrationConfigUpdateRequest, EXPERIMENT_MAX_TARGETED_USERS,
|
||||
ExperimentDeliveryConfigUpdateRequest, GatewayRolloutConfigUpdateRequest,
|
||||
GatewayRolloutMode, InstanceAttachmentDecayUpdateRequest,
|
||||
InstanceBlueskyIntegrationUpdateRequest, InstanceBlueskyKeyIntegrationUpdateRequest,
|
||||
InstanceCaptchaIntegrationUpdateRequest, InstanceConfigUpdateRequest,
|
||||
InstanceEmailIntegrationUpdateRequest, InstanceEmailSmtpIntegrationUpdateRequest,
|
||||
InstanceEmailSmtpTestRequest, InstanceGifIntegrationUpdateRequest,
|
||||
InstanceIntegrationsUpdateRequest, InstanceMediaUpdateRequest,
|
||||
InstancePolicyUpdateRequest, InstanceRegistrationConfigUpdateRequest,
|
||||
InstanceServicesUpdateRequest, InstanceYoutubeIntegrationUpdateRequest,
|
||||
LimitConfigUpdateRequest, LimitRule, LimitRuleFilters, PremiumMode,
|
||||
ProfileTimezoneConfigUpdateRequest, PushRelayConfigUpdateRequest, RegistrationMode,
|
||||
SsoConfigUpdateRequest, VoiceE2eeScope,
|
||||
CreateRegistrationUrlRequest, DomainMigrationConfigUpdateRequest,
|
||||
EXPERIMENT_MAX_TARGETED_USERS, ExperimentDeliveryConfigUpdateRequest,
|
||||
GatewayRolloutConfigUpdateRequest, GatewayRolloutMode,
|
||||
InstanceAttachmentDecayUpdateRequest, InstanceBlueskyIntegrationUpdateRequest,
|
||||
InstanceBlueskyKeyIntegrationUpdateRequest, InstanceCaptchaIntegrationUpdateRequest,
|
||||
InstanceConfigUpdateRequest, InstanceEmailIntegrationUpdateRequest,
|
||||
InstanceEmailSmtpIntegrationUpdateRequest, InstanceEmailSmtpTestRequest,
|
||||
InstanceGifIntegrationUpdateRequest, InstanceIntegrationsUpdateRequest,
|
||||
InstanceMediaUpdateRequest, InstancePolicyUpdateRequest,
|
||||
InstanceRegistrationConfigUpdateRequest, InstanceServicesUpdateRequest,
|
||||
InstanceYoutubeIntegrationUpdateRequest, LimitConfigUpdateRequest, LimitRule,
|
||||
LimitRuleFilters, PremiumMode, ProfileTimezoneConfigUpdateRequest,
|
||||
PushRelayConfigUpdateRequest, RegistrationMode, SsoConfigUpdateRequest, VoiceE2eeScope,
|
||||
},
|
||||
},
|
||||
config::AdminConfig,
|
||||
@@ -808,7 +807,6 @@ fn build_policy_update(form: &MultiValueForm) -> InstanceConfigUpdateRequest {
|
||||
_ => None,
|
||||
};
|
||||
let services = build_services_update(form);
|
||||
let deferred_phone_gate = build_deferred_phone_gate_update(form);
|
||||
InstanceConfigUpdateRequest {
|
||||
policy: Some(InstancePolicyUpdateRequest {
|
||||
single_community_enabled: None,
|
||||
@@ -816,36 +814,11 @@ fn build_policy_update(form: &MultiValueForm) -> InstanceConfigUpdateRequest {
|
||||
direct_messages_disabled,
|
||||
premium_mode,
|
||||
services,
|
||||
deferred_phone_gate,
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn build_deferred_phone_gate_update(
|
||||
form: &MultiValueForm,
|
||||
) -> Option<DeferredPhoneGateUpdateRequest> {
|
||||
let enabled = form
|
||||
.first("policy_deferred_phone_gate_enabled")
|
||||
.map(|value| value == "true");
|
||||
let window_hours = form
|
||||
.first("policy_deferred_phone_gate_window_hours")
|
||||
.and_then(|value| value.parse::<f64>().ok())
|
||||
.filter(|value| *value > 0.0);
|
||||
let member_threshold = form
|
||||
.first("policy_deferred_phone_gate_member_threshold")
|
||||
.and_then(|value| value.parse::<i64>().ok())
|
||||
.filter(|value| *value > 0);
|
||||
if enabled.is_none() && window_hours.is_none() && member_threshold.is_none() {
|
||||
return None;
|
||||
}
|
||||
Some(DeferredPhoneGateUpdateRequest {
|
||||
enabled,
|
||||
window_hours,
|
||||
member_threshold,
|
||||
})
|
||||
}
|
||||
|
||||
fn build_services_update(form: &MultiValueForm) -> Option<InstanceServicesUpdateRequest> {
|
||||
let parse_tristate = |key: &str| match form.first(key) {
|
||||
Some("inherit") => Some(None),
|
||||
@@ -980,7 +953,6 @@ fn build_single_community_update(enabled: bool) -> InstanceConfigUpdateRequest {
|
||||
direct_messages_disabled: None,
|
||||
premium_mode: None,
|
||||
services: None,
|
||||
deferred_phone_gate: None,
|
||||
}),
|
||||
..Default::default()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use crate::{
|
||||
admin_flags, api::client::AdminApiClient, middleware::flash::FlashData,
|
||||
admin_flags,
|
||||
api::client::{AdminApiClient, ApiError},
|
||||
middleware::flash::FlashData,
|
||||
utils::forms::MultiValueForm,
|
||||
};
|
||||
use std::collections::HashSet;
|
||||
@@ -303,11 +305,53 @@ pub async fn dispatch(
|
||||
"Failed to schedule user deletion",
|
||||
)
|
||||
}
|
||||
"cancel_deletion" => DispatchOutcome::from_result(
|
||||
client.cancel_deletion(user_id).await,
|
||||
"User deletion cancelled successfully",
|
||||
"Failed to cancel user deletion",
|
||||
),
|
||||
"cancel_deletion" => {
|
||||
let Some(expected) = get("expected_pending_deletion_at") else {
|
||||
return DispatchOutcome::error(
|
||||
"The pending deletion is missing from the form. Reload and review.",
|
||||
);
|
||||
};
|
||||
if !form.bool_value("confirm") {
|
||||
return DispatchOutcome::error(
|
||||
"Confirm whose deletion you are cancelling before submitting",
|
||||
);
|
||||
}
|
||||
let Some(private_reason) = get("private_reason") else {
|
||||
return DispatchOutcome::error("A private reason is required to cancel a deletion");
|
||||
};
|
||||
let notify_user = form.bool_value("notify_user");
|
||||
match client
|
||||
.cancel_deletion(user_id, &expected, notify_user, Some(&private_reason))
|
||||
.await
|
||||
{
|
||||
Ok(_) => DispatchOutcome::success("User deletion cancelled successfully"),
|
||||
Err(ApiError::Http { status: 409, .. }) => DispatchOutcome::error(
|
||||
"The pending deletion changed since this page loaded. Reload and review.",
|
||||
),
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, user_id, "admin API request failed: cancel user deletion");
|
||||
DispatchOutcome::error("Failed to cancel user deletion")
|
||||
}
|
||||
}
|
||||
}
|
||||
"annotate_ban" => {
|
||||
let Some(ban_audit_log_id) = get("ban_audit_log_id") else {
|
||||
return DispatchOutcome::error("The ban audit log entry is missing from the form");
|
||||
};
|
||||
let Some(note) = get("note") else {
|
||||
return DispatchOutcome::error("Note is required");
|
||||
};
|
||||
match client.annotate_ban(user_id, &ban_audit_log_id, ¬e).await {
|
||||
Ok(()) => DispatchOutcome::success("Note added to the ban"),
|
||||
Err(ApiError::Http { status: 409, .. }) => DispatchOutcome::error(
|
||||
"The ban changed since this page loaded. Reload and review.",
|
||||
),
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, user_id, "admin API request failed: annotate ban");
|
||||
DispatchOutcome::error("Failed to add the note to the ban")
|
||||
}
|
||||
}
|
||||
}
|
||||
"change_dob" => {
|
||||
let Some(dob) = get("date_of_birth") else {
|
||||
return DispatchOutcome::error("Date of birth is required");
|
||||
|
||||
@@ -111,11 +111,47 @@ pub async fn render(
|
||||
query.delete_all_messages_channel_count.unwrap_or(0),
|
||||
query.delete_all_messages_message_count.unwrap_or(0),
|
||||
));
|
||||
let deletion_scheduler = match u.deletion_scheduled_by.as_deref() {
|
||||
Some(scheduler_id) if u.pending_deletion_at.is_some() && scheduler_id != u.id => {
|
||||
client
|
||||
.get_user_by_id(scheduler_id)
|
||||
.await
|
||||
.log_error("load deletion scheduler")
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let ban_logs = if u.temp_banned_until.is_some()
|
||||
&& acl::has_permission(admin_acls, acl::AUDIT_LOG_VIEW)
|
||||
{
|
||||
client
|
||||
.search_audit_logs(&SearchAuditLogsParams {
|
||||
query: None,
|
||||
admin_user_id: None,
|
||||
target_id: Some(user_id.to_owned()),
|
||||
target_type: Some("user".to_owned()),
|
||||
access: Some("write".to_owned()),
|
||||
sort_by: Some("created_at".to_owned()),
|
||||
sort_order: Some("desc".to_owned()),
|
||||
limit: 100,
|
||||
offset: 0,
|
||||
})
|
||||
.await
|
||||
.log_error("load ban audit logs")
|
||||
.map(|response| response.logs)
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let context = tabs::moderation::ModerationContext {
|
||||
deletion_scheduler: deletion_scheduler.as_ref(),
|
||||
current_ban: tabs::moderation::find_current_ban(&u, &ban_logs),
|
||||
};
|
||||
Some(tabs::moderation::moderation_tab(
|
||||
config,
|
||||
&u,
|
||||
csrf_token,
|
||||
admin_acls,
|
||||
&context,
|
||||
query.message_shred_job_id.as_deref(),
|
||||
message_shred_status.as_ref(),
|
||||
delete_all_messages_dry_run,
|
||||
|
||||
Reference in new issue
Block a user