mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
172 lines
5.2 KiB
Rust
172 lines
5.2 KiB
Rust
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
use super::ActionQuery;
|
|
use crate::{
|
|
acl,
|
|
api::client::{AdminApiClient, ApiResultExt},
|
|
config::AdminConfig,
|
|
middleware::{
|
|
auth::AuthContext,
|
|
csrf::CsrfToken,
|
|
flash::{self, FlashData},
|
|
htmx,
|
|
},
|
|
state::AppState,
|
|
templates,
|
|
utils::forms::MultiValueForm,
|
|
};
|
|
use axum::{
|
|
Router,
|
|
extract::{Query, Request, State},
|
|
response::{Html, IntoResponse, Response},
|
|
routing::get,
|
|
};
|
|
|
|
pub fn router() -> Router<AppState> {
|
|
Router::new().route(
|
|
"/admin-api-keys",
|
|
get(admin_api_keys_page).post(admin_api_keys_post),
|
|
)
|
|
}
|
|
|
|
async fn admin_api_keys_page(
|
|
State(state): State<AppState>,
|
|
auth: axum::Extension<AuthContext>,
|
|
csrf: axum::Extension<CsrfToken>,
|
|
flash: Option<axum::Extension<FlashData>>,
|
|
) -> Response {
|
|
let config = state.config();
|
|
let client = AdminApiClient::new(state.http_client(), config, &auth.0.session);
|
|
let keys = client
|
|
.list_api_keys()
|
|
.await
|
|
.log_error("load admin api keys");
|
|
let available_acls = available_acls(&auth.0);
|
|
let flash = flash.map(|flash| flash.0.to_flash_message());
|
|
let markup = templates::pages::admin_api_keys::admin_api_keys_page(
|
|
config,
|
|
&auth.0,
|
|
&csrf.0.0,
|
|
flash.as_ref(),
|
|
None,
|
|
keys.as_deref(),
|
|
&available_acls,
|
|
);
|
|
Html(markup.into_string()).into_response()
|
|
}
|
|
|
|
async fn admin_api_keys_post(
|
|
State(state): State<AppState>,
|
|
auth: axum::Extension<AuthContext>,
|
|
csrf: axum::Extension<CsrfToken>,
|
|
Query(aq): Query<ActionQuery>,
|
|
request: Request,
|
|
) -> Response {
|
|
let config = state.config();
|
|
let base = &config.base_path;
|
|
let is_htmx = htmx::is_htmx_request(request.headers());
|
|
let form = match MultiValueForm::from_request(request).await {
|
|
Some(form) => form,
|
|
None => {
|
|
return admin_api_key_flash_response(
|
|
config,
|
|
FlashData::error("Invalid form data"),
|
|
is_htmx,
|
|
);
|
|
}
|
|
};
|
|
let client = AdminApiClient::new(state.http_client(), config, &auth.0.session);
|
|
let action = aq.action.as_deref().unwrap_or("");
|
|
match action {
|
|
"create" => {
|
|
let name = form.clean("name").unwrap_or_default();
|
|
let acls = form.list_values_any(&["acls[]", "acls"]);
|
|
match client.create_api_key(&name, &acls).await {
|
|
Ok(created) => {
|
|
let keys = client
|
|
.list_api_keys()
|
|
.await
|
|
.log_error("reload admin api keys after create");
|
|
let available_acls = available_acls(&auth.0);
|
|
let markup = templates::pages::admin_api_keys::admin_api_keys_page(
|
|
config,
|
|
&auth.0,
|
|
&csrf.0.0,
|
|
None,
|
|
Some(&created),
|
|
keys.as_deref(),
|
|
&available_acls,
|
|
);
|
|
Html(markup.into_string()).into_response()
|
|
}
|
|
Err(error) => {
|
|
tracing::warn!(%error, "admin API request failed: create API key");
|
|
admin_api_key_flash_response(
|
|
config,
|
|
FlashData::error("Failed to create API key."),
|
|
is_htmx,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
"revoke" => {
|
|
let Some(key_id) = form.clean("key_id") else {
|
|
return admin_api_key_flash_response(
|
|
config,
|
|
FlashData::error("API key ID is required"),
|
|
is_htmx,
|
|
);
|
|
};
|
|
let result = client.revoke_api_key(&key_id).await;
|
|
let flash = match result.log_error("revoke API key") {
|
|
Some(_) => FlashData::success("API key revoked."),
|
|
None => FlashData::error("Failed to revoke API key"),
|
|
};
|
|
flash::redirect_with_flash(
|
|
&format!("{base}/admin-api-keys"),
|
|
flash,
|
|
config.secure_cookies(),
|
|
)
|
|
}
|
|
"" => admin_api_key_flash_response(
|
|
config,
|
|
FlashData::error("API key action is required"),
|
|
is_htmx,
|
|
),
|
|
_ => admin_api_key_flash_response(
|
|
config,
|
|
FlashData::error("Unknown API key action"),
|
|
is_htmx,
|
|
),
|
|
}
|
|
}
|
|
|
|
fn admin_api_key_flash_response(
|
|
config: &AdminConfig,
|
|
flash_data: FlashData,
|
|
is_htmx: bool,
|
|
) -> Response {
|
|
if is_htmx {
|
|
htmx::toast_response(&flash_data)
|
|
} else {
|
|
flash::redirect_with_flash(
|
|
&format!("{}/admin-api-keys", config.base_path),
|
|
flash_data,
|
|
config.secure_cookies(),
|
|
)
|
|
}
|
|
}
|
|
|
|
fn available_acls(auth: &AuthContext) -> Vec<&'static str> {
|
|
let admin_acls = auth
|
|
.admin_user
|
|
.as_ref()
|
|
.map(|user| user.acls.as_slice())
|
|
.unwrap_or(&[]);
|
|
acl::ALL_ACLS
|
|
.iter()
|
|
.copied()
|
|
.filter(|item| acl::has_permission(admin_acls, item))
|
|
.collect()
|
|
}
|