mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
feat(admin): move pending registrations to their own page (#1388)
This commit is contained in:
1 parent
97b2ac91ca
commit
abcb5cb814
31 files changed
+839
-328
No files matched your search
@@ -13,6 +13,7 @@ pub mod guilds;
|
||||
pub mod jobs;
|
||||
mod message_actions;
|
||||
pub mod messages;
|
||||
pub mod registrations;
|
||||
pub mod reports;
|
||||
pub mod system;
|
||||
mod system_actions;
|
||||
@@ -63,6 +64,7 @@ pub fn build_router(config: AdminConfig) -> Router {
|
||||
.merge(applications::router())
|
||||
.merge(codes::router())
|
||||
.merge(discovery::router())
|
||||
.merge(registrations::router())
|
||||
.merge(jobs::router())
|
||||
.merge(messages::router())
|
||||
.merge(system::router())
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use crate::{
|
||||
api::{client::AdminApiClient, types::PendingRegistrationStatus},
|
||||
middleware::{
|
||||
auth::AuthContext,
|
||||
csrf::CsrfToken,
|
||||
flash::{self, FlashData},
|
||||
htmx,
|
||||
},
|
||||
state::AppState,
|
||||
templates::{self, layout_sidebar::pending_registration_count_markup},
|
||||
utils::forms::clean_string,
|
||||
};
|
||||
use axum::{
|
||||
Form, Router,
|
||||
extract::State,
|
||||
http::HeaderMap,
|
||||
response::{Html, IntoResponse, Response},
|
||||
routing::{get, post},
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct PendingRegistrationForm {
|
||||
#[serde(default)]
|
||||
user_id: Option<String>,
|
||||
}
|
||||
|
||||
pub fn router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/pending-registrations", get(pending_registrations_page))
|
||||
.route(
|
||||
"/pending-registrations/approve",
|
||||
post(approve_pending_registration),
|
||||
)
|
||||
.route(
|
||||
"/pending-registrations/reject",
|
||||
post(reject_pending_registration),
|
||||
)
|
||||
}
|
||||
|
||||
async fn pending_registrations_page(
|
||||
State(state): State<AppState>,
|
||||
auth: axum::Extension<AuthContext>,
|
||||
csrf: axum::Extension<CsrfToken>,
|
||||
) -> Response {
|
||||
let config = state.config();
|
||||
let client = AdminApiClient::new(state.http_client(), config, &auth.0.session);
|
||||
let pending_registrations = match client.list_pending_registrations().await {
|
||||
Ok(pending_registrations) => Some(pending_registrations),
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "admin API request failed: list pending registrations");
|
||||
None
|
||||
}
|
||||
};
|
||||
let markup = templates::pages::pending_registrations::pending_registrations_page(
|
||||
config,
|
||||
&auth.0,
|
||||
&csrf.0.0,
|
||||
pending_registrations.as_deref(),
|
||||
);
|
||||
Html(markup.into_string()).into_response()
|
||||
}
|
||||
|
||||
async fn approve_pending_registration(
|
||||
state: State<AppState>,
|
||||
headers: HeaderMap,
|
||||
auth: axum::Extension<AuthContext>,
|
||||
csrf: axum::Extension<CsrfToken>,
|
||||
form: Form<PendingRegistrationForm>,
|
||||
) -> Response {
|
||||
decide_pending_registration(
|
||||
state,
|
||||
headers,
|
||||
auth,
|
||||
csrf,
|
||||
form,
|
||||
PendingRegistrationStatus::Approved,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn reject_pending_registration(
|
||||
state: State<AppState>,
|
||||
headers: HeaderMap,
|
||||
auth: axum::Extension<AuthContext>,
|
||||
csrf: axum::Extension<CsrfToken>,
|
||||
form: Form<PendingRegistrationForm>,
|
||||
) -> Response {
|
||||
decide_pending_registration(
|
||||
state,
|
||||
headers,
|
||||
auth,
|
||||
csrf,
|
||||
form,
|
||||
PendingRegistrationStatus::Rejected,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn decide_pending_registration(
|
||||
State(state): State<AppState>,
|
||||
headers: HeaderMap,
|
||||
auth: axum::Extension<AuthContext>,
|
||||
csrf: axum::Extension<CsrfToken>,
|
||||
Form(form): Form<PendingRegistrationForm>,
|
||||
status: PendingRegistrationStatus,
|
||||
) -> Response {
|
||||
let config = state.config();
|
||||
let (success_message, error_message) = match status {
|
||||
PendingRegistrationStatus::Approved => {
|
||||
("Registration approved", "Failed to approve registration")
|
||||
}
|
||||
PendingRegistrationStatus::Rejected => {
|
||||
("Registration rejected", "Failed to reject registration")
|
||||
}
|
||||
};
|
||||
let flash = match clean_string(form.user_id.as_deref().unwrap_or("")) {
|
||||
None => FlashData::error("User ID is required"),
|
||||
Some(user_id) => {
|
||||
let client = AdminApiClient::new(state.http_client(), config, &auth.0.session);
|
||||
match client.decide_pending_registration(&user_id, status).await {
|
||||
Ok(pending_registrations) => {
|
||||
let flash = FlashData::success(success_message);
|
||||
if htmx::targets(&headers, "pending-registration-list") {
|
||||
let markup = maud::html! {
|
||||
(templates::pages::pending_registrations::pending_registration_list(
|
||||
config,
|
||||
&csrf.0.0,
|
||||
&pending_registrations,
|
||||
))
|
||||
(pending_registration_count_markup(
|
||||
u32::try_from(pending_registrations.len()).ok(),
|
||||
true,
|
||||
))
|
||||
};
|
||||
let mut response = Html(markup.into_string()).into_response();
|
||||
htmx::add_toast_header(&mut response, &flash);
|
||||
return response;
|
||||
}
|
||||
flash
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, ?status, "admin API request failed: decide pending registration");
|
||||
FlashData::error(error_message)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if htmx::is_htmx_request(&headers) {
|
||||
return htmx::toast_response(&flash);
|
||||
}
|
||||
flash::redirect_with_flash(
|
||||
&format!("{}/pending-registrations", config.base_path),
|
||||
flash,
|
||||
config.secure_cookies(),
|
||||
)
|
||||
}
|
||||
@@ -304,48 +304,6 @@ pub async fn instance_config_post(
|
||||
},
|
||||
None => FlashData::error("Registration URL ID is required"),
|
||||
},
|
||||
"approve_pending_registration" => match form.clean("user_id") {
|
||||
Some(user_id) => match client.approve_pending_registration(&user_id).await {
|
||||
Ok(instance_config) => {
|
||||
let flash = FlashData::success("Registration approved");
|
||||
if htmx::targets(&headers, "pending-registration-list") {
|
||||
return render_pending_registration_list_response(
|
||||
config,
|
||||
&csrf.0.0,
|
||||
&instance_config,
|
||||
&flash,
|
||||
);
|
||||
}
|
||||
flash
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "admin API request failed: approve pending registration");
|
||||
FlashData::error("Failed to approve registration")
|
||||
}
|
||||
},
|
||||
None => FlashData::error("User ID is required"),
|
||||
},
|
||||
"reject_pending_registration" => match form.clean("user_id") {
|
||||
Some(user_id) => match client.reject_pending_registration(&user_id).await {
|
||||
Ok(instance_config) => {
|
||||
let flash = FlashData::success("Registration rejected");
|
||||
if htmx::targets(&headers, "pending-registration-list") {
|
||||
return render_pending_registration_list_response(
|
||||
config,
|
||||
&csrf.0.0,
|
||||
&instance_config,
|
||||
&flash,
|
||||
);
|
||||
}
|
||||
flash
|
||||
}
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "admin API request failed: reject pending registration");
|
||||
FlashData::error("Failed to reject registration")
|
||||
}
|
||||
},
|
||||
None => FlashData::error("User ID is required"),
|
||||
},
|
||||
_ => FlashData::error("Unknown instance config action"),
|
||||
};
|
||||
if htmx::is_htmx_request(&headers) {
|
||||
@@ -381,22 +339,6 @@ fn render_registration_url_list_response(
|
||||
)
|
||||
}
|
||||
|
||||
fn render_pending_registration_list_response(
|
||||
config: &AdminConfig,
|
||||
csrf_token: &str,
|
||||
instance_config: &crate::api::types::InstanceConfigResponse,
|
||||
flash: &FlashData,
|
||||
) -> Response {
|
||||
render_fragment_with_toast(
|
||||
templates::pages::instance_config::pending_registration_list(
|
||||
config,
|
||||
csrf_token,
|
||||
&instance_config.registration.pending_registrations,
|
||||
),
|
||||
flash,
|
||||
)
|
||||
}
|
||||
|
||||
fn render_fragment_with_toast(markup: Markup, flash: &FlashData) -> Response {
|
||||
let mut response = Html(markup.into_string()).into_response();
|
||||
htmx::add_toast_header(&mut response, flash);
|
||||
|
||||
Reference in new issue
Block a user