This commit is contained in:
2026-08-30 16:08:57 +02:00
parent f2197a6b28
commit 78cb70d8e8
28 changed files with 740 additions and 42 deletions
+81 -3
View File
@@ -1,5 +1,4 @@
use crate::models::{role, server, server_user};
use crate::repositories::Repositories;
use crate::models::{role, role_user, server, server_user};
use crate::services::ServicesContext;
use sea_orm::{
ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect, Set,
@@ -21,7 +20,9 @@ impl ServerService {
pub async fn create_server(
&self,
name: String,
password: Option<String>,
is_default: bool,
owner_id: Uuid,
) -> Result<server::Model, anyhow::Error> {
let db = &self.service_context.repositories.server.context.db;
let event_bus = &self.service_context.event_bus;
@@ -30,7 +31,9 @@ impl ServerService {
let active = server::ActiveModel {
name: Set(name),
password: Set(password),
is_default: Set(is_default),
owner_id: Set(Some(owner_id)),
..Default::default()
};
let srv = active.insert(&txn).await?;
@@ -41,7 +44,22 @@ impl ServerService {
is_default: Set(true),
..Default::default()
};
default_group.insert(&txn).await?;
let default_group = default_group.insert(&txn).await?;
server_user::ActiveModel {
server_id: Set(srv.id),
user_id: Set(owner_id),
..Default::default()
}
.insert(&txn)
.await?;
role_user::ActiveModel {
role_id: Set(default_group.id),
user_id: Set(owner_id),
}
.insert(&txn)
.await?;
txn.commit().await?;
@@ -50,6 +68,66 @@ impl ServerService {
Ok(srv)
}
pub async fn join_server(
&self,
server_id: Uuid,
user_id: Uuid,
password: Option<String>,
) -> Result<server::Model, anyhow::Error> {
let db = &self.service_context.repositories.server.context.db;
let event_bus = &self.service_context.event_bus;
let txn = db.begin().await?;
let srv = server::Entity::find_by_id(server_id)
.one(&txn)
.await?
.ok_or_else(|| anyhow::anyhow!("Server not found"))?;
if srv.password != password {
return Err(anyhow::anyhow!("Invalid server password"));
}
if server_user::Entity::find()
.filter(server_user::Column::ServerId.eq(server_id))
.filter(server_user::Column::UserId.eq(user_id))
.one(&txn)
.await?
.is_some()
{
txn.commit().await?;
return Ok(srv);
}
server_user::ActiveModel {
server_id: Set(server_id),
user_id: Set(user_id),
..Default::default()
}
.insert(&txn)
.await?;
let default_role = role::Entity::find()
.filter(role::Column::ServerId.eq(server_id))
.filter(role::Column::IsDefault.eq(true))
.one(&txn)
.await?
.ok_or_else(|| anyhow::anyhow!("Default role not found"))?;
role_user::ActiveModel {
role_id: Set(default_role.id),
user_id: Set(user_id),
}
.insert(&txn)
.await?;
txn.commit().await?;
event_bus.emit("server_user_created", (server_id, user_id));
self.service_context
.repositories
.read_state
.mark_server_read(server_id, user_id)
.await?;
Ok(srv)
}
pub async fn update_server(
&self,
id: Uuid,
+142 -1
View File
@@ -1,7 +1,10 @@
use crate::domain::dto::server_item_order::{ReorderServerItemRequest, ServerItemOrderPosition};
use crate::models::server_item_order::{self, OrderedResourceType};
use crate::models::{category, channel};
use crate::services::ServicesContext;
use sea_orm::{
ActiveModelTrait, ColumnTrait, DatabaseTransaction, EntityTrait, QueryFilter, QuerySelect, Set,
ActiveModelTrait, ColumnTrait, DatabaseTransaction, EntityTrait, QueryFilter, QueryOrder,
QuerySelect, Set, TransactionTrait,
};
use std::sync::Arc;
use uuid::Uuid;
@@ -107,6 +110,144 @@ impl ServerOrderService {
Ok(())
}
pub async fn reorder(&self, request: ReorderServerItemRequest) -> Result<(), anyhow::Error> {
let db = &self.service_context.repositories.server.context.db;
let txn = db.begin().await?;
let mut orders = server_item_order::Entity::find()
.filter(server_item_order::Column::ServerId.eq(request.server_id))
.order_by_asc(server_item_order::Column::ParentCategoryId)
.order_by_asc(server_item_order::Column::OrderKey)
.all(&txn)
.await?;
let target_index = orders
.iter()
.position(|order| {
order.resource_id == request.resource_id
&& order.resource_type == request.resource_type
})
.ok_or_else(|| anyhow::anyhow!("Ordered resource not found"))?;
let target = orders.remove(target_index);
if request.resource_type == OrderedResourceType::Category
&& request.parent_category_id.is_some()
{
return Err(anyhow::anyhow!("A category cannot have a parent category"));
}
if let Some(category_id) = request.parent_category_id {
let parent = category::Entity::find_by_id(category_id)
.one(&txn)
.await?
.ok_or_else(|| anyhow::anyhow!("Parent category not found"))?;
if parent.server_id != request.server_id {
return Err(anyhow::anyhow!("Parent category belongs to another server"));
}
}
if request.resource_type == OrderedResourceType::Channel {
let channel_model = channel::Entity::find_by_id(request.resource_id)
.one(&txn)
.await?
.ok_or_else(|| anyhow::anyhow!("Channel not found"))?;
if channel_model.server_id != Some(request.server_id) {
return Err(anyhow::anyhow!("Channel belongs to another server"));
}
if channel_model.category_id != request.parent_category_id {
let mut active: channel::ActiveModel = channel_model.into();
active.category_id = Set(request.parent_category_id);
active.update(&txn).await?;
}
} else {
let category_model = category::Entity::find_by_id(request.resource_id)
.one(&txn)
.await?
.ok_or_else(|| anyhow::anyhow!("Category not found"))?;
if category_model.server_id != request.server_id {
return Err(anyhow::anyhow!("Category belongs to another server"));
}
}
let reference_index = request.reference.as_ref().map(|reference| {
orders.iter().position(|order| {
order.resource_id == reference.resource_id
&& order.resource_type == reference.resource_type
})
});
let Some(reference_index) = reference_index.flatten() else {
if request.reference.is_some() {
return Err(anyhow::anyhow!("Order reference not found"));
}
let insertion_index = orders
.iter()
.filter(|order| order.parent_category_id == request.parent_category_id)
.count();
let destination_indices: Vec<usize> = orders
.iter()
.enumerate()
.filter_map(|(index, order)| {
(order.parent_category_id == request.parent_category_id).then_some(index)
})
.collect();
let insert_at = destination_indices
.get(insertion_index.saturating_sub(1))
.map(|index| index + 1)
.unwrap_or_else(|| {
orders
.iter()
.position(|order| order.parent_category_id == request.parent_category_id)
.unwrap_or(orders.len())
});
orders.insert(
insert_at,
server_item_order::Model {
parent_category_id: request.parent_category_id,
order_key: target.order_key,
..target
},
);
self.persist_reordered(&txn, orders).await?;
txn.commit().await?;
return Ok(());
};
let reference = orders[reference_index].clone();
if reference.parent_category_id != request.parent_category_id {
return Err(anyhow::anyhow!("Order reference belongs to another list"));
}
let insert_at = match request.position {
ServerItemOrderPosition::Before => reference_index,
ServerItemOrderPosition::After => reference_index + 1,
};
orders.insert(
insert_at,
server_item_order::Model {
parent_category_id: request.parent_category_id,
order_key: target.order_key,
..target
},
);
self.persist_reordered(&txn, orders).await?;
txn.commit().await?;
Ok(())
}
async fn persist_reordered(
&self,
txn: &DatabaseTransaction,
orders: Vec<server_item_order::Model>,
) -> Result<(), anyhow::Error> {
for (index, order) in orders.into_iter().enumerate() {
let mut active: server_item_order::ActiveModel = order.into();
active.order_key = Set((index as i64) + 1);
active.update(txn).await?;
}
Ok(())
}
async fn next_order_key(
&self,
txn: &DatabaseTransaction,