diff --git a/CHANGELOG.md b/CHANGELOG.md index 134b98e..62bea74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Management UI at `GET /manage` for adding, editing, and deleting emotes. +- `GET /manage/emotes` — admin JSON endpoint returning full emote data (uuid, + name, alias, url, timestamps) for use by the management UI. +- `AdminEmoteResponse` model exposing `uuid` and `alias` fields not present in + the public `EmoteResponse`. +- Client-side search on the management page filtering emotes by name or alias. + ## [0.2.4] - 2026-04-11 ### Fixed diff --git a/src/main.rs b/src/main.rs index 0677932..a81ad23 100644 --- a/src/main.rs +++ b/src/main.rs @@ -60,6 +60,8 @@ async fn main() { .route("/emotes", post(routes::emotes::create_emote)) .route("/emotes/{uuid}", put(routes::emotes::update_emote)) .route("/emotes/{uuid}", delete(routes::emotes::delete_emote)) + .route("/manage", get(routes::manage::manage_root)) + .route("/manage/emotes", get(routes::manage::list_admin_emotes)) .layer(TraceLayer::new_for_http()) .with_state(state); diff --git a/src/models.rs b/src/models.rs index ed06816..e3e5f28 100644 --- a/src/models.rs +++ b/src/models.rs @@ -40,6 +40,17 @@ pub struct EmoteResponse { pub modified: DateTime, } +/// Extended JSON representation used by the management API (includes uuid and alias). +#[derive(Debug, Serialize, Deserialize)] +pub struct AdminEmoteResponse { + pub uuid: String, + pub name: String, + pub alias: Option, + pub url: String, + pub created: DateTime, + pub modified: DateTime, +} + /// Payload for updating an existing emote. #[derive(Debug, Deserialize)] pub struct UpdateEmoteRequest { diff --git a/src/routes/manage.rs b/src/routes/manage.rs new file mode 100644 index 0000000..b3d9a92 --- /dev/null +++ b/src/routes/manage.rs @@ -0,0 +1,43 @@ +use axum::{ + extract::State, + http::StatusCode, + response::{Html, IntoResponse, Json}, +}; +use serde_json::json; + +use crate::{models::AdminEmoteResponse, AppState}; + +/// GET /manage +/// Serves the emote management HTML page. +pub async fn manage_root() -> impl IntoResponse { + Html(include_str!("../templates/manage.html")) +} + +/// GET /manage/emotes +/// Returns all emotes with full admin data (uuid, alias included). +pub async fn list_admin_emotes(State(state): State) -> impl IntoResponse { + match state.db.list_emotes().await { + Ok(rows) => { + let emotes: Vec = rows + .into_iter() + .map(|row| AdminEmoteResponse { + uuid: row.uuid.clone(), + name: row.name.clone(), + alias: row.alias.clone(), + url: state.storage.public_url(&row.image_key), + created: row.created_dt(), + modified: row.modified_dt(), + }) + .collect(); + (StatusCode::OK, Json(json!({"emotes": emotes}))).into_response() + } + Err(e) => { + tracing::error!("Failed to list emotes: {e}"); + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({"error": "Failed to list emotes"})), + ) + .into_response() + } + } +} diff --git a/src/routes/mod.rs b/src/routes/mod.rs index 4cb0bc3..5e1340f 100644 --- a/src/routes/mod.rs +++ b/src/routes/mod.rs @@ -1,3 +1,4 @@ pub mod emotes; pub mod health; +pub mod manage; pub mod version; diff --git a/src/templates/manage.html b/src/templates/manage.html new file mode 100644 index 0000000..8bc6baf --- /dev/null +++ b/src/templates/manage.html @@ -0,0 +1,420 @@ + + + + + +MIKEBASE — Manage + + + + + +
+
+ + + + +
+ +
+ +
+ + + + + + + + + + +
CodeAlias
+ +
+ + +
+ +
+ + +
+ +
+ +
+ + + +