feat(spaces): add default roles init and startup cache rebuild
Add ensure_default_roles() to check if a Space has m.space.roles state event and create default admin/mod roles if missing. Add worker() to rebuild the space roles cache on startup by iterating all rooms and populating cache for spaces. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e3a0ab2214
commit
c8f39ca6ff
1 changed files with 103 additions and 2 deletions
|
|
@ -12,7 +12,12 @@ use std::{
|
|||
};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use conduwuit::{Event, Result, Server, debug_warn, implement, matrix::pdu::PduBuilder, warn};
|
||||
use conduwuit::{
|
||||
Event, Result, Server, debug, debug_warn, implement, info,
|
||||
matrix::pdu::PduBuilder,
|
||||
warn,
|
||||
};
|
||||
use serde_json::value::to_raw_value;
|
||||
use conduwuit_core::{
|
||||
matrix::space_roles::{
|
||||
RoleDefinition, SpaceRoleMemberEventContent, SpaceRoleRoomEventContent,
|
||||
|
|
@ -25,7 +30,7 @@ use conduwuit_core::{
|
|||
};
|
||||
use futures::{StreamExt, TryFutureExt};
|
||||
use ruma::{
|
||||
Int, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId,
|
||||
Int, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId, room::RoomType,
|
||||
events::{
|
||||
StateEventType,
|
||||
room::{
|
||||
|
|
@ -54,6 +59,7 @@ pub struct Service {
|
|||
|
||||
struct Services {
|
||||
globals: Dep<globals::Service>,
|
||||
metadata: Dep<rooms::metadata::Service>,
|
||||
state_accessor: Dep<rooms::state_accessor::Service>,
|
||||
state_cache: Dep<rooms::state_cache::Service>,
|
||||
state: Dep<rooms::state::Service>,
|
||||
|
|
@ -68,6 +74,7 @@ impl crate::Service for Service {
|
|||
Ok(Arc::new(Self {
|
||||
services: Services {
|
||||
globals: args.depend::<globals::Service>("globals"),
|
||||
metadata: args.depend::<rooms::metadata::Service>("rooms::metadata"),
|
||||
state_accessor: args
|
||||
.depend::<rooms::state_accessor::Service>("rooms::state_accessor"),
|
||||
state_cache: args.depend::<rooms::state_cache::Service>("rooms::state_cache"),
|
||||
|
|
@ -104,6 +111,37 @@ impl crate::Service for Service {
|
|||
self.room_to_space.write().await.clear();
|
||||
}
|
||||
|
||||
async fn worker(self: Arc<Self>) -> Result<()> {
|
||||
if !self.is_enabled() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("Rebuilding space roles cache from all known rooms...");
|
||||
|
||||
let mut space_count: usize = 0;
|
||||
let room_ids: Vec<OwnedRoomId> = self
|
||||
.services
|
||||
.metadata
|
||||
.iter_ids()
|
||||
.map(ToOwned::to_owned)
|
||||
.collect()
|
||||
.await;
|
||||
|
||||
for room_id in &room_ids {
|
||||
match self.services.state_accessor.get_room_type(room_id).await {
|
||||
| Ok(RoomType::Space) => {
|
||||
debug!("Populating space roles cache for {room_id}");
|
||||
self.populate_space(room_id).await;
|
||||
space_count += 1;
|
||||
},
|
||||
| _ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
info!("Space roles cache rebuilt for {space_count} spaces");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
|
||||
}
|
||||
|
||||
|
|
@ -111,6 +149,69 @@ impl crate::Service for Service {
|
|||
#[implement(Service)]
|
||||
pub fn is_enabled(&self) -> bool { self.server.config.space_permission_cascading }
|
||||
|
||||
/// Ensure a Space has the default admin/mod roles defined.
|
||||
///
|
||||
/// Checks whether an `m.space.roles` state event exists in the given space.
|
||||
/// If not, creates default roles (admin at PL 100, mod at PL 50) and sends
|
||||
/// the state event as the server user.
|
||||
#[implement(Service)]
|
||||
pub async fn ensure_default_roles(&self, space_id: &RoomId) -> Result {
|
||||
if !self.is_enabled() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Check if m.space.roles already exists
|
||||
let roles_event_type = StateEventType::from("m.space.roles".to_owned());
|
||||
if self
|
||||
.services
|
||||
.state_accessor
|
||||
.room_state_get_content::<SpaceRolesEventContent>(space_id, &roles_event_type, "")
|
||||
.await
|
||||
.is_ok()
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Create default roles
|
||||
let mut roles = BTreeMap::new();
|
||||
roles.insert(
|
||||
"admin".to_owned(),
|
||||
RoleDefinition {
|
||||
description: "Space administrator".to_owned(),
|
||||
power_level: Some(100),
|
||||
},
|
||||
);
|
||||
roles.insert(
|
||||
"mod".to_owned(),
|
||||
RoleDefinition {
|
||||
description: "Space moderator".to_owned(),
|
||||
power_level: Some(50),
|
||||
},
|
||||
);
|
||||
|
||||
let content = SpaceRolesEventContent { roles };
|
||||
|
||||
let sender = self.services.globals.server_user.as_ref();
|
||||
let state_lock = self.services.state.mutex.lock(space_id).await;
|
||||
|
||||
let pdu = PduBuilder {
|
||||
event_type: ruma::events::TimelineEventType::from("m.space.roles".to_owned()),
|
||||
content: to_raw_value(&content)
|
||||
.expect("Failed to serialize SpaceRolesEventContent"),
|
||||
state_key: Some(String::new().into()),
|
||||
..PduBuilder::default()
|
||||
};
|
||||
|
||||
self.services
|
||||
.timeline
|
||||
.build_and_append_pdu(pdu, sender, Some(space_id), &state_lock)
|
||||
.await?;
|
||||
|
||||
debug!("Sent default m.space.roles event for {space_id}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Populate the in-memory caches from state events for a single Space room.
|
||||
///
|
||||
/// Reads `m.space.roles`, `m.space.role.member`, `m.space.role.room`, and
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue