diff --git a/apps/chattyness-app/src/app.rs b/apps/chattyness-app/src/app.rs
index 75ba5dc..bf7a632 100644
--- a/apps/chattyness-app/src/app.rs
+++ b/apps/chattyness-app/src/app.rs
@@ -4,16 +4,14 @@
//! with the admin interface lazy-loaded to reduce initial WASM bundle size.
use leptos::prelude::*;
-use leptos_meta::{provide_meta_context, MetaTags, Stylesheet, Title};
+use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context};
use leptos_router::{
- components::{Route, Router, Routes},
ParamSegment, StaticSegment,
+ components::{Route, Router, Routes},
};
// Re-export user pages for inline route definitions
-use chattyness_user_ui::pages::{
- HomePage, LoginPage, PasswordResetPage, RealmPage, SignupPage,
-};
+use chattyness_user_ui::pages::{HomePage, LoginPage, PasswordResetPage, RealmPage, SignupPage};
// Lazy-load admin pages to split WASM bundle
// Each lazy function includes the admin CSS stylesheet for on-demand loading
@@ -34,7 +32,8 @@ fn lazy_login() -> AnyView {
- }.into_any()
+ }
+ .into_any()
}
#[lazy]
diff --git a/apps/chattyness-app/src/lib.rs b/apps/chattyness-app/src/lib.rs
index 96ab28a..29fbb86 100644
--- a/apps/chattyness-app/src/lib.rs
+++ b/apps/chattyness-app/src/lib.rs
@@ -6,7 +6,7 @@
mod app;
-pub use app::{combined_shell, CombinedApp};
+pub use app::{CombinedApp, combined_shell};
#[cfg(feature = "ssr")]
pub use app::CombinedAppState;
diff --git a/apps/chattyness-app/src/main.rs b/apps/chattyness-app/src/main.rs
index 79019da..fcec49f 100644
--- a/apps/chattyness-app/src/main.rs
+++ b/apps/chattyness-app/src/main.rs
@@ -11,7 +11,7 @@ mod server {
use axum::Router;
use clap::Parser;
use leptos::prelude::*;
- use leptos_axum::{generate_route_list, LeptosRoutes};
+ use leptos_axum::{LeptosRoutes, generate_route_list};
use sqlx::postgres::PgPoolOptions;
use std::net::SocketAddr;
use std::path::{Path, PathBuf};
@@ -20,7 +20,7 @@ mod server {
use tower_http::services::ServeDir;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
- use chattyness_app::{combined_shell, CombinedApp, CombinedAppState};
+ use chattyness_app::{CombinedApp, CombinedAppState, combined_shell};
use chattyness_shared::AppConfig;
use chattyness_user_ui::api::WebSocketState;
@@ -57,8 +57,9 @@ mod server {
// Initialize logging
tracing_subscriber::registry()
.with(
- tracing_subscriber::EnvFilter::try_from_default_env()
- .unwrap_or_else(|_| "chattyness_app=debug,chattyness_user_ui=debug,tower_http=debug".into()),
+ tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
+ "chattyness_app=debug,chattyness_user_ui=debug,tower_http=debug".into()
+ }),
)
.with(tracing_subscriber::fmt::layer())
.init();
@@ -100,9 +101,8 @@ mod server {
let cleanup_pool = pool.clone();
let cleanup_config = config.cleanup.clone();
tokio::spawn(async move {
- let mut interval = tokio::time::interval(Duration::from_secs(
- cleanup_config.reap_interval_secs,
- ));
+ let mut interval =
+ tokio::time::interval(Duration::from_secs(cleanup_config.reap_interval_secs));
loop {
interval.tick().await;
let threshold = cleanup_config.stale_threshold_secs as f64;
@@ -138,9 +138,11 @@ mod server {
let addr = SocketAddr::new(args.host.parse()?, args.port);
// Create session layer (shared between user and admin interfaces)
- let session_layer =
- chattyness_user_ui::auth::session::create_session_layer(pool.clone(), args.secure_cookies)
- .await;
+ let session_layer = chattyness_user_ui::auth::session::create_session_layer(
+ pool.clone(),
+ args.secure_cookies,
+ )
+ .await;
// Create combined app state
let app_state = CombinedAppState {
@@ -183,10 +185,9 @@ mod server {
};
// Build nested API routers with their own state
- let user_api_router = chattyness_user_ui::api::api_router()
- .with_state(user_api_state);
- let admin_api_router = chattyness_admin_ui::api::admin_api_router()
- .with_state(admin_api_state);
+ let user_api_router = chattyness_user_ui::api::api_router().with_state(user_api_state);
+ let admin_api_router =
+ chattyness_admin_ui::api::admin_api_router().with_state(admin_api_state);
// Create RLS layer for row-level security
let rls_layer = chattyness_user_ui::auth::RlsLayer::new(pool.clone());
@@ -216,16 +217,30 @@ mod server {
.with_state(app_state)
// Serve pkg files at /pkg (wasm_split hardcodes /pkg/ imports)
// Fallback to split_pkg_dir for --split mode output
- .nest_service("/pkg", ServeDir::new(&pkg_dir).fallback(ServeDir::new(&split_pkg_dir)))
+ .nest_service(
+ "/pkg",
+ ServeDir::new(&pkg_dir).fallback(ServeDir::new(&split_pkg_dir)),
+ )
// Uploaded assets (realm backgrounds, etc.) - must come before /static
.nest_service("/static/realm", ServeDir::new(assets_dir.join("realm")))
// Server-level assets (avatar props, etc.)
.nest_service("/static/server", ServeDir::new(assets_dir.join("server")))
// Also serve at /static for backwards compatibility
- .nest_service("/static", ServeDir::new(&pkg_dir).fallback(ServeDir::new(&split_pkg_dir)))
- .nest_service("/favicon.ico", tower_http::services::ServeFile::new(public_dir.join("favicon.ico")))
+ .nest_service(
+ "/static",
+ ServeDir::new(&pkg_dir).fallback(ServeDir::new(&split_pkg_dir)),
+ )
+ .nest_service(
+ "/favicon.ico",
+ tower_http::services::ServeFile::new(public_dir.join("favicon.ico")),
+ )
+ // Serve icons from public/icons
+ .nest_service("/icons", ServeDir::new(public_dir.join("icons")))
// Serve admin CSS at standardized path (symlinked from owner build)
- .nest_service("/static/css/admin.css", tower_http::services::ServeFile::new(public_dir.join("admin.css")))
+ .nest_service(
+ "/static/css/admin.css",
+ tower_http::services::ServeFile::new(public_dir.join("admin.css")),
+ )
// Apply middleware layers (order: session outer, rls inner)
.layer(rls_layer)
.layer(session_layer);
diff --git a/apps/chattyness-owner/src/main.rs b/apps/chattyness-owner/src/main.rs
index ecfa53b..65dcdf1 100644
--- a/apps/chattyness-owner/src/main.rs
+++ b/apps/chattyness-owner/src/main.rs
@@ -6,17 +6,17 @@
#[cfg(feature = "ssr")]
mod server {
- use axum::{response::Redirect, routing::get, Router};
+ use axum::{Router, response::Redirect, routing::get};
use clap::Parser;
use leptos::prelude::*;
- use leptos_axum::{generate_route_list, LeptosRoutes};
+ use leptos_axum::{LeptosRoutes, generate_route_list};
use sqlx::postgres::PgPoolOptions;
use std::net::SocketAddr;
use std::path::Path;
use tower_http::services::ServeDir;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
- use chattyness_admin_ui::{admin_shell, AdminApp, AdminAppState};
+ use chattyness_admin_ui::{AdminApp, AdminAppState, admin_shell};
/// CLI arguments.
#[derive(Parser)]
@@ -77,9 +77,11 @@ mod server {
let addr = SocketAddr::new(args.host.parse()?, args.port);
// Create session layer
- let session_layer =
- chattyness_admin_ui::auth::create_admin_session_layer(pool.clone(), args.secure_cookies)
- .await;
+ let session_layer = chattyness_admin_ui::auth::create_admin_session_layer(
+ pool.clone(),
+ args.secure_cookies,
+ )
+ .await;
// Create app state
let app_state = AdminAppState {
@@ -111,15 +113,24 @@ mod server {
// Redirect root to admin
.route("/", get(|| async { Redirect::permanent("/admin") }))
// Nest API routes under /api/admin (matches frontend expectations when UI is at /admin)
- .nest("/api/admin", chattyness_admin_ui::api::admin_api_router().with_state(app_state.clone()))
+ .nest(
+ "/api/admin",
+ chattyness_admin_ui::api::admin_api_router().with_state(app_state.clone()),
+ )
// Uploaded assets (realm backgrounds, props, etc.) - must come before /static
.nest_service("/assets/server", ServeDir::new(assets_dir.join("server")))
.nest_service("/static/realm", ServeDir::new(assets_dir.join("realm")))
// Static files (build output: JS, CSS, WASM)
.nest_service("/static", ServeDir::new(&static_dir))
- .nest_service("/favicon.ico", tower_http::services::ServeFile::new(&favicon_path))
+ .nest_service(
+ "/favicon.ico",
+ tower_http::services::ServeFile::new(&favicon_path),
+ )
// Serve admin CSS at standardized path
- .nest_service("/static/css/admin.css", tower_http::services::ServeFile::new(&admin_css_path))
+ .nest_service(
+ "/static/css/admin.css",
+ tower_http::services::ServeFile::new(&admin_css_path),
+ )
// Leptos routes
.leptos_routes(&app_state, routes, {
let leptos_options = leptos_options.clone();
diff --git a/crates/chattyness-admin-ui/src/api/auth.rs b/crates/chattyness-admin-ui/src/api/auth.rs
index 497c1b6..05b834a 100644
--- a/crates/chattyness-admin-ui/src/api/auth.rs
+++ b/crates/chattyness-admin-ui/src/api/auth.rs
@@ -1,6 +1,6 @@
//! Admin authentication API handlers.
-use axum::{extract::State, http::StatusCode, Json};
+use axum::{Json, extract::State, http::StatusCode};
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use tower_sessions::Session;
@@ -221,21 +221,16 @@ pub async fn get_auth_context(
session: Session,
) -> Result, (StatusCode, Json)> {
// Try to get staff_id from session (server staff)
- let staff_id: Option = session
- .get(ADMIN_SESSION_STAFF_ID_KEY)
- .await
- .ok()
- .flatten();
+ let staff_id: Option = session.get(ADMIN_SESSION_STAFF_ID_KEY).await.ok().flatten();
if let Some(staff_id) = staff_id {
// Check if this is actually a staff member
- let is_staff: Option = sqlx::query_scalar(
- "SELECT EXISTS(SELECT 1 FROM server.staff WHERE user_id = $1)",
- )
- .bind(staff_id)
- .fetch_one(&pool)
- .await
- .ok();
+ let is_staff: Option =
+ sqlx::query_scalar("SELECT EXISTS(SELECT 1 FROM server.staff WHERE user_id = $1)")
+ .bind(staff_id)
+ .fetch_one(&pool)
+ .await
+ .ok();
if is_staff == Some(true) {
return Ok(Json(AuthContextResponse {
diff --git a/crates/chattyness-admin-ui/src/api/config.rs b/crates/chattyness-admin-ui/src/api/config.rs
index 2546faa..7b69dbc 100644
--- a/crates/chattyness-admin-ui/src/api/config.rs
+++ b/crates/chattyness-admin-ui/src/api/config.rs
@@ -1,6 +1,6 @@
//! Server config API handlers.
-use axum::{extract::State, Json};
+use axum::{Json, extract::State};
use chattyness_db::{
models::{ServerConfig, UpdateServerConfigRequest},
queries::owner as queries,
@@ -9,9 +9,7 @@ use chattyness_error::AppError;
use sqlx::PgPool;
/// Get server config.
-pub async fn get_config(
- State(pool): State,
-) -> Result, AppError> {
+pub async fn get_config(State(pool): State) -> Result, AppError> {
let config = queries::get_server_config(&pool).await?;
Ok(Json(config))
}
diff --git a/crates/chattyness-admin-ui/src/api/dashboard.rs b/crates/chattyness-admin-ui/src/api/dashboard.rs
index 2dd1ac1..150add3 100644
--- a/crates/chattyness-admin-ui/src/api/dashboard.rs
+++ b/crates/chattyness-admin-ui/src/api/dashboard.rs
@@ -1,6 +1,6 @@
//! Dashboard API handlers.
-use axum::{extract::State, Json};
+use axum::{Json, extract::State};
use chattyness_error::AppError;
use serde::Serialize;
use sqlx::PgPool;
@@ -16,9 +16,7 @@ pub struct DashboardStats {
}
/// Get dashboard stats.
-pub async fn get_stats(
- State(pool): State,
-) -> Result, AppError> {
+pub async fn get_stats(State(pool): State) -> Result, AppError> {
// Total users
let total_users: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM auth.users")
.fetch_one(&pool)
diff --git a/crates/chattyness-admin-ui/src/api/props.rs b/crates/chattyness-admin-ui/src/api/props.rs
index b018666..bad536a 100644
--- a/crates/chattyness-admin-ui/src/api/props.rs
+++ b/crates/chattyness-admin-ui/src/api/props.rs
@@ -1,14 +1,14 @@
//! Props management API handlers for admin UI.
-use axum::extract::{Query, State};
use axum::Json;
+use axum::extract::{Query, State};
use axum_extra::extract::Multipart;
-use serde::Deserialize;
use chattyness_db::{
models::{CreateServerPropRequest, ServerProp, ServerPropSummary},
queries::props,
};
use chattyness_error::AppError;
+use serde::Deserialize;
use serde::Serialize;
use sha2::{Digest, Sha256};
use sqlx::PgPool;
@@ -64,9 +64,7 @@ fn validate_file_extension(filename: &str) -> Result<&'static str, AppError> {
match ext.as_str() {
"svg" => Ok("svg"),
"png" => Ok("png"),
- _ => Err(AppError::Validation(
- "File must be SVG or PNG".to_string(),
- )),
+ _ => Err(AppError::Validation("File must be SVG or PNG".to_string())),
}
}
@@ -101,7 +99,9 @@ async fn store_prop_file(bytes: &[u8], extension: &str) -> Result) -> Result>, AppError> {
+pub async fn list_props(
+ State(pool): State,
+) -> Result>, AppError> {
let prop_list = props::list_server_props(&pool).await?;
Ok(Json(prop_list))
}
@@ -137,9 +137,10 @@ pub async fn create_prop(
.await
.map_err(|e| AppError::Validation(format!("Failed to read metadata: {}", e)))?;
- metadata = Some(serde_json::from_str(&text).map_err(|e| {
- AppError::Validation(format!("Invalid metadata JSON: {}", e))
- })?);
+ metadata =
+ Some(serde_json::from_str(&text).map_err(|e| {
+ AppError::Validation(format!("Invalid metadata JSON: {}", e))
+ })?);
}
"file" => {
let filename = field
diff --git a/crates/chattyness-admin-ui/src/api/realms.rs b/crates/chattyness-admin-ui/src/api/realms.rs
index 72ad45d..d2f31ff 100644
--- a/crates/chattyness-admin-ui/src/api/realms.rs
+++ b/crates/chattyness-admin-ui/src/api/realms.rs
@@ -1,8 +1,8 @@
//! Realm management API handlers.
use axum::{
- extract::{Path, Query, State},
Json,
+ extract::{Path, Query, State},
};
use chattyness_db::{
models::{OwnerCreateRealmRequest, RealmDetail, RealmListItem, UpdateRealmRequest},
diff --git a/crates/chattyness-admin-ui/src/api/routes.rs b/crates/chattyness-admin-ui/src/api/routes.rs
index b8e7055..fda79d0 100644
--- a/crates/chattyness-admin-ui/src/api/routes.rs
+++ b/crates/chattyness-admin-ui/src/api/routes.rs
@@ -1,8 +1,8 @@
//! Admin API routes.
use axum::{
- routing::{delete, get, post, put},
Router,
+ routing::{delete, get, post, put},
};
use super::{auth, config, dashboard, props, realms, scenes, spots, staff, users};
@@ -56,10 +56,7 @@ pub fn admin_api_router() -> Router {
"/realms/{slug}",
get(realms::get_realm).put(realms::update_realm),
)
- .route(
- "/realms/{slug}/transfer",
- post(realms::transfer_ownership),
- )
+ .route("/realms/{slug}/transfer", post(realms::transfer_ownership))
// API - Scenes
.route(
"/realms/{slug}/scenes",
diff --git a/crates/chattyness-admin-ui/src/api/scenes.rs b/crates/chattyness-admin-ui/src/api/scenes.rs
index 62d11e7..cebc006 100644
--- a/crates/chattyness-admin-ui/src/api/scenes.rs
+++ b/crates/chattyness-admin-ui/src/api/scenes.rs
@@ -1,8 +1,8 @@
//! Scene management API handlers for admin UI.
use axum::{
- extract::{Path, State},
Json,
+ extract::{Path, State},
};
use chattyness_db::{
models::{CreateSceneRequest, Scene, SceneSummary, UpdateSceneRequest},
@@ -136,10 +136,7 @@ async fn download_and_store_image(
.map_err(|e| AppError::Internal(format!("Failed to write image file: {}", e)))?;
// Return the URL path (relative to server root)
- let local_path = format!(
- "/static/realm/{}/scene/{}/{}",
- realm_id, scene_id, filename
- );
+ let local_path = format!("/static/realm/{}/scene/{}/{}", realm_id, scene_id, filename);
Ok(ImageDownloadResult {
local_path,
@@ -237,13 +234,9 @@ pub async fn create_scene(
// Handle background image URL - download and store locally
if let Some(ref url) = req.background_image_url {
if !url.is_empty() {
- let result = download_and_store_image(
- url,
- realm.id,
- scene_id,
- req.infer_dimensions_from_image,
- )
- .await?;
+ let result =
+ download_and_store_image(url, realm.id, scene_id, req.infer_dimensions_from_image)
+ .await?;
req.background_image_path = Some(result.local_path);
diff --git a/crates/chattyness-admin-ui/src/api/spots.rs b/crates/chattyness-admin-ui/src/api/spots.rs
index db83212..527f86b 100644
--- a/crates/chattyness-admin-ui/src/api/spots.rs
+++ b/crates/chattyness-admin-ui/src/api/spots.rs
@@ -1,8 +1,8 @@
//! Spot management API handlers for admin UI.
use axum::{
- extract::{Path, State},
Json,
+ extract::{Path, State},
};
use chattyness_db::{
models::{CreateSpotRequest, Spot, SpotSummary, UpdateSpotRequest},
@@ -73,7 +73,8 @@ pub async fn update_spot(
.ok_or_else(|| AppError::NotFound("Spot not found".to_string()))?;
if Some(new_slug.clone()) != existing.slug {
- let available = spots::is_spot_slug_available(&pool, existing.scene_id, new_slug).await?;
+ let available =
+ spots::is_spot_slug_available(&pool, existing.scene_id, new_slug).await?;
if !available {
return Err(AppError::Conflict(format!(
"Spot slug '{}' is already taken in this scene",
diff --git a/crates/chattyness-admin-ui/src/api/staff.rs b/crates/chattyness-admin-ui/src/api/staff.rs
index daf9f9b..bcc0477 100644
--- a/crates/chattyness-admin-ui/src/api/staff.rs
+++ b/crates/chattyness-admin-ui/src/api/staff.rs
@@ -1,8 +1,8 @@
//! Staff management API handlers.
use axum::{
- extract::{Path, State},
Json,
+ extract::{Path, State},
};
use chattyness_db::{
models::{CreateStaffRequest, StaffMember},
@@ -21,9 +21,7 @@ pub struct CreateStaffResponse {
}
/// List all staff members.
-pub async fn list_staff(
- State(pool): State,
-) -> Result>, AppError> {
+pub async fn list_staff(State(pool): State) -> Result>, AppError> {
let staff = queries::get_all_staff(&pool).await?;
Ok(Json(staff))
}
@@ -44,11 +42,7 @@ pub async fn create_staff(
}))
} else if let Some(ref new_user) = req.new_user {
// Create new user and promote to staff
- let (user_id, temporary_password) = queries::create_user(
- &pool,
- new_user,
- )
- .await?;
+ let (user_id, temporary_password) = queries::create_user(&pool, new_user).await?;
let staff = queries::create_staff(&pool, user_id, req.role, None).await?;
Ok(Json(CreateStaffResponse {
staff,
diff --git a/crates/chattyness-admin-ui/src/api/users.rs b/crates/chattyness-admin-ui/src/api/users.rs
index 837645e..4f61fba 100644
--- a/crates/chattyness-admin-ui/src/api/users.rs
+++ b/crates/chattyness-admin-ui/src/api/users.rs
@@ -1,8 +1,8 @@
//! User management API handlers.
use axum::{
- extract::{Path, Query, State},
Json,
+ extract::{Path, Query, State},
};
use chattyness_db::{
models::{
@@ -153,9 +153,7 @@ pub async fn remove_from_realm(
}
/// List all realms (for dropdown).
-pub async fn list_realms(
- State(pool): State,
-) -> Result>, AppError> {
+pub async fn list_realms(State(pool): State) -> Result>, AppError> {
let realms = queries::list_all_realms(&pool).await?;
Ok(Json(realms))
}
diff --git a/crates/chattyness-admin-ui/src/app.rs b/crates/chattyness-admin-ui/src/app.rs
index cac693b..602288b 100644
--- a/crates/chattyness-admin-ui/src/app.rs
+++ b/crates/chattyness-admin-ui/src/app.rs
@@ -1,7 +1,7 @@
//! Admin Leptos application root and router.
use leptos::prelude::*;
-use leptos_meta::{provide_meta_context, MetaTags, Stylesheet, Title};
+use leptos_meta::{MetaTags, Stylesheet, Title, provide_meta_context};
use leptos_router::components::Router;
use crate::routes::AdminRoutes;
diff --git a/crates/chattyness-admin-ui/src/auth.rs b/crates/chattyness-admin-ui/src/auth.rs
index adefe45..fc328a2 100644
--- a/crates/chattyness-admin-ui/src/auth.rs
+++ b/crates/chattyness-admin-ui/src/auth.rs
@@ -12,7 +12,7 @@ use axum::{
#[cfg(feature = "ssr")]
use sqlx::PgPool;
#[cfg(feature = "ssr")]
-use tower_sessions::{cookie::time::Duration, cookie::SameSite, Expiry, SessionManagerLayer};
+use tower_sessions::{Expiry, SessionManagerLayer, cookie::SameSite, cookie::time::Duration};
#[cfg(feature = "ssr")]
use tower_sessions_sqlx_store::PostgresStore;
#[cfg(feature = "ssr")]
diff --git a/crates/chattyness-admin-ui/src/components.rs b/crates/chattyness-admin-ui/src/components.rs
index 0551aed..1931959 100644
--- a/crates/chattyness-admin-ui/src/components.rs
+++ b/crates/chattyness-admin-ui/src/components.rs
@@ -110,8 +110,7 @@ pub fn use_auth_context() -> LocalResource