add initial crates and apps
This commit is contained in:
parent
5c87ba3519
commit
1ca300098f
113 changed files with 28169 additions and 0 deletions
180
crates/chattyness-db/src/queries/props.rs
Normal file
180
crates/chattyness-db/src/queries/props.rs
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
//! Props-related database queries.
|
||||
|
||||
use sqlx::PgExecutor;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::models::{CreateServerPropRequest, ServerProp, ServerPropSummary};
|
||||
use chattyness_error::AppError;
|
||||
|
||||
/// List all server props.
|
||||
pub async fn list_server_props<'e>(
|
||||
executor: impl PgExecutor<'e>,
|
||||
) -> Result<Vec<ServerPropSummary>, AppError> {
|
||||
let props = sqlx::query_as::<_, ServerPropSummary>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
slug,
|
||||
asset_path,
|
||||
default_layer,
|
||||
is_active,
|
||||
created_at
|
||||
FROM server.props
|
||||
ORDER BY name ASC
|
||||
"#,
|
||||
)
|
||||
.fetch_all(executor)
|
||||
.await?;
|
||||
|
||||
Ok(props)
|
||||
}
|
||||
|
||||
/// Get a server prop by ID.
|
||||
pub async fn get_server_prop_by_id<'e>(
|
||||
executor: impl PgExecutor<'e>,
|
||||
prop_id: Uuid,
|
||||
) -> Result<Option<ServerProp>, AppError> {
|
||||
let prop = sqlx::query_as::<_, ServerProp>(
|
||||
r#"
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
slug,
|
||||
description,
|
||||
tags,
|
||||
asset_path,
|
||||
thumbnail_path,
|
||||
default_layer,
|
||||
default_emotion,
|
||||
default_position,
|
||||
is_unique,
|
||||
is_transferable,
|
||||
is_portable,
|
||||
is_active,
|
||||
available_from,
|
||||
available_until,
|
||||
created_by,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM server.props
|
||||
WHERE id = $1
|
||||
"#,
|
||||
)
|
||||
.bind(prop_id)
|
||||
.fetch_optional(executor)
|
||||
.await?;
|
||||
|
||||
Ok(prop)
|
||||
}
|
||||
|
||||
/// Check if a prop slug is available.
|
||||
pub async fn is_prop_slug_available<'e>(
|
||||
executor: impl PgExecutor<'e>,
|
||||
slug: &str,
|
||||
) -> Result<bool, AppError> {
|
||||
let exists: (bool,) =
|
||||
sqlx::query_as(r#"SELECT EXISTS(SELECT 1 FROM server.props WHERE slug = $1)"#)
|
||||
.bind(slug)
|
||||
.fetch_one(executor)
|
||||
.await?;
|
||||
|
||||
Ok(!exists.0)
|
||||
}
|
||||
|
||||
/// Create a new server prop.
|
||||
pub async fn create_server_prop<'e>(
|
||||
executor: impl PgExecutor<'e>,
|
||||
req: &CreateServerPropRequest,
|
||||
asset_path: &str,
|
||||
created_by: Option<Uuid>,
|
||||
) -> Result<ServerProp, AppError> {
|
||||
let slug = req.slug_or_generate();
|
||||
|
||||
// Positioning: either content layer OR emotion layer OR neither (all NULL)
|
||||
// Database constraint enforces mutual exclusivity
|
||||
let (default_layer, default_emotion, default_position) =
|
||||
if req.default_layer.is_some() {
|
||||
// Content layer prop
|
||||
(
|
||||
req.default_layer.map(|l| l.to_string()),
|
||||
None,
|
||||
Some(req.default_position.unwrap_or(4)), // Default to center position
|
||||
)
|
||||
} else if req.default_emotion.is_some() {
|
||||
// Emotion layer prop
|
||||
(
|
||||
None,
|
||||
req.default_emotion.map(|e| e.to_string()),
|
||||
Some(req.default_position.unwrap_or(4)), // Default to center position
|
||||
)
|
||||
} else {
|
||||
// Non-avatar prop
|
||||
(None, None, None)
|
||||
};
|
||||
|
||||
let prop = sqlx::query_as::<_, ServerProp>(
|
||||
r#"
|
||||
INSERT INTO server.props (
|
||||
name, slug, description, tags, asset_path,
|
||||
default_layer, default_emotion, default_position,
|
||||
created_by
|
||||
)
|
||||
VALUES (
|
||||
$1, $2, $3, $4, $5,
|
||||
$6::props.avatar_layer, $7::props.emotion_state, $8,
|
||||
$9
|
||||
)
|
||||
RETURNING
|
||||
id,
|
||||
name,
|
||||
slug,
|
||||
description,
|
||||
tags,
|
||||
asset_path,
|
||||
thumbnail_path,
|
||||
default_layer,
|
||||
default_emotion,
|
||||
default_position,
|
||||
is_unique,
|
||||
is_transferable,
|
||||
is_portable,
|
||||
is_active,
|
||||
available_from,
|
||||
available_until,
|
||||
created_by,
|
||||
created_at,
|
||||
updated_at
|
||||
"#,
|
||||
)
|
||||
.bind(&req.name)
|
||||
.bind(&slug)
|
||||
.bind(&req.description)
|
||||
.bind(&req.tags)
|
||||
.bind(asset_path)
|
||||
.bind(&default_layer)
|
||||
.bind(&default_emotion)
|
||||
.bind(default_position)
|
||||
.bind(created_by)
|
||||
.fetch_one(executor)
|
||||
.await?;
|
||||
|
||||
Ok(prop)
|
||||
}
|
||||
|
||||
/// Delete a server prop.
|
||||
pub async fn delete_server_prop<'e>(
|
||||
executor: impl PgExecutor<'e>,
|
||||
prop_id: Uuid,
|
||||
) -> Result<(), AppError> {
|
||||
let result = sqlx::query(r#"DELETE FROM server.props WHERE id = $1"#)
|
||||
.bind(prop_id)
|
||||
.execute(executor)
|
||||
.await?;
|
||||
|
||||
if result.rows_affected() == 0 {
|
||||
return Err(AppError::NotFound("Prop not found".to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue