108 lines
3 KiB
Rust
108 lines
3 KiB
Rust
//! Spot management API handlers for admin UI.
|
|
|
|
use axum::{
|
|
Json,
|
|
extract::{Path, State},
|
|
};
|
|
use chattyness_db::{
|
|
models::{CreateSpotRequest, Spot, SpotSummary, UpdateSpotRequest},
|
|
queries::spots,
|
|
};
|
|
use chattyness_error::AppError;
|
|
use serde::Serialize;
|
|
use sqlx::PgPool;
|
|
use uuid::Uuid;
|
|
|
|
use crate::auth::AdminConn;
|
|
|
|
/// List all spots for a scene.
|
|
pub async fn list_spots(
|
|
State(pool): State<PgPool>,
|
|
Path(scene_id): Path<Uuid>,
|
|
) -> Result<Json<Vec<SpotSummary>>, AppError> {
|
|
let spot_list = spots::list_spots_for_scene(&pool, scene_id).await?;
|
|
Ok(Json(spot_list))
|
|
}
|
|
|
|
/// Get a spot by ID.
|
|
pub async fn get_spot(
|
|
State(pool): State<PgPool>,
|
|
Path(spot_id): Path<Uuid>,
|
|
) -> Result<Json<Spot>, AppError> {
|
|
let spot = spots::get_spot_by_id(&pool, spot_id)
|
|
.await?
|
|
.ok_or_else(|| AppError::NotFound("Spot not found".to_string()))?;
|
|
Ok(Json(spot))
|
|
}
|
|
|
|
/// Create spot response.
|
|
#[derive(Debug, Serialize)]
|
|
pub struct CreateSpotResponse {
|
|
pub id: Uuid,
|
|
}
|
|
|
|
/// Create a new spot in a scene.
|
|
pub async fn create_spot(
|
|
admin_conn: AdminConn,
|
|
Path(scene_id): Path<Uuid>,
|
|
Json(req): Json<CreateSpotRequest>,
|
|
) -> Result<Json<CreateSpotResponse>, AppError> {
|
|
let conn = admin_conn.0;
|
|
let mut guard = conn.acquire().await;
|
|
|
|
// Check if slug is available (if provided)
|
|
if let Some(ref slug) = req.slug {
|
|
let available = spots::is_spot_slug_available(&mut *guard, scene_id, slug).await?;
|
|
if !available {
|
|
return Err(AppError::Conflict(format!(
|
|
"Spot slug '{}' is already taken in this scene",
|
|
slug
|
|
)));
|
|
}
|
|
}
|
|
|
|
let spot = spots::create_spot(&mut *guard, scene_id, &req).await?;
|
|
Ok(Json(CreateSpotResponse { id: spot.id }))
|
|
}
|
|
|
|
/// Update a spot.
|
|
pub async fn update_spot(
|
|
admin_conn: AdminConn,
|
|
Path(spot_id): Path<Uuid>,
|
|
Json(req): Json<UpdateSpotRequest>,
|
|
) -> Result<Json<Spot>, AppError> {
|
|
let conn = admin_conn.0;
|
|
let mut guard = conn.acquire().await;
|
|
|
|
// If updating slug, check availability
|
|
if let Some(ref new_slug) = req.slug {
|
|
let existing = spots::get_spot_by_id(&mut *guard, spot_id)
|
|
.await?
|
|
.ok_or_else(|| AppError::NotFound("Spot not found".to_string()))?;
|
|
|
|
if Some(new_slug.clone()) != existing.slug {
|
|
let available =
|
|
spots::is_spot_slug_available(&mut *guard, existing.scene_id, new_slug).await?;
|
|
if !available {
|
|
return Err(AppError::Conflict(format!(
|
|
"Spot slug '{}' is already taken in this scene",
|
|
new_slug
|
|
)));
|
|
}
|
|
}
|
|
}
|
|
|
|
let spot = spots::update_spot(&mut *guard, spot_id, &req).await?;
|
|
Ok(Json(spot))
|
|
}
|
|
|
|
/// Delete a spot.
|
|
pub async fn delete_spot(
|
|
admin_conn: AdminConn,
|
|
Path(spot_id): Path<Uuid>,
|
|
) -> Result<Json<()>, AppError> {
|
|
let conn = admin_conn.0;
|
|
let mut guard = conn.acquire().await;
|
|
spots::delete_spot(&mut *guard, spot_id).await?;
|
|
Ok(Json(()))
|
|
}
|