//! Plugins API — list, install, remove, enable, disable. //! //! All mutation operations use POST with a JSON body containing `name` //! to avoid path-parameter routing conflicts with Axum 0.7/matchit 0.7. use axum::{extract::State, http::StatusCode, Json}; use serde::{Deserialize, Serialize}; use el_plugin_host::Plugin; use crate::AppState; type ApiResult = Result, (StatusCode, Json)>; fn api_err(code: StatusCode, msg: impl Into) -> (StatusCode, Json) { (code, Json(serde_json::json!({ "error": msg.into() }))) } #[derive(Debug, Deserialize)] pub struct NameRequest { pub name: String, } #[derive(Debug, Serialize)] pub struct OkResponse { pub ok: bool, } /// GET /api/plugins pub async fn list_plugins(State(state): State) -> ApiResult> { let host = state.plugins.lock().await; Ok(Json(host.list())) } /// POST /api/plugins/install — body: { name } pub async fn install_plugin( State(state): State, Json(req): Json, ) -> ApiResult { let mut host = state.plugins.lock().await; host.install(&req.name) .map(Json) .map_err(|e| api_err(StatusCode::BAD_REQUEST, e.to_string())) } /// POST /api/plugins/remove — body: { name } pub async fn remove_plugin( State(state): State, Json(req): Json, ) -> ApiResult { let mut host = state.plugins.lock().await; host.remove(&req.name) .map(|_| Json(OkResponse { ok: true })) .map_err(|e| api_err(StatusCode::BAD_REQUEST, e.to_string())) } /// POST /api/plugins/enable — body: { name } pub async fn enable_plugin( State(state): State, Json(req): Json, ) -> ApiResult { let mut host = state.plugins.lock().await; host.enable(&req.name) .map(|_| Json(OkResponse { ok: true })) .map_err(|e| api_err(StatusCode::BAD_REQUEST, e.to_string())) } /// POST /api/plugins/disable — body: { name } pub async fn disable_plugin( State(state): State, Json(req): Json, ) -> ApiResult { let mut host = state.plugins.lock().await; host.disable(&req.name) .map(|_| Json(OkResponse { ok: true })) .map_err(|e| api_err(StatusCode::BAD_REQUEST, e.to_string())) }