mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2025-09-11 21:13:02 +02:00
Compare commits
9 commits
c639228f4d
...
607caddacf
Author | SHA1 | Date | |
---|---|---|---|
|
607caddacf |
||
|
6691b7672b |
||
|
b75842ab88 |
||
|
e586dcd8f2 |
||
|
390c1ab18e |
||
|
38301980e0 |
||
|
58de3a905e |
||
|
97f490bb00 |
||
|
f7410283cf |
2 changed files with 145 additions and 31 deletions
|
@ -2,10 +2,10 @@ use std::cmp::max;
|
||||||
|
|
||||||
use axum::extract::State;
|
use axum::extract::State;
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, Event, Result, err, info,
|
Err, Error, Event, Result, debug, err, info,
|
||||||
matrix::{StateKey, pdu::PduBuilder},
|
matrix::{StateKey, pdu::PduBuilder},
|
||||||
};
|
};
|
||||||
use futures::StreamExt;
|
use futures::{FutureExt, StreamExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonObject, RoomId, RoomVersionId,
|
CanonicalJsonObject, RoomId, RoomVersionId,
|
||||||
api::client::{error::ErrorKind, room::upgrade_room},
|
api::client::{error::ErrorKind, room::upgrade_room},
|
||||||
|
@ -16,15 +16,16 @@ use ruma::{
|
||||||
power_levels::RoomPowerLevelsEventContent,
|
power_levels::RoomPowerLevelsEventContent,
|
||||||
tombstone::RoomTombstoneEventContent,
|
tombstone::RoomTombstoneEventContent,
|
||||||
},
|
},
|
||||||
|
space::child::{RedactedSpaceChildEventContent, SpaceChildEventContent},
|
||||||
},
|
},
|
||||||
int,
|
int,
|
||||||
};
|
};
|
||||||
use serde_json::{json, value::to_raw_value};
|
use serde_json::{json, value::to_raw_value};
|
||||||
|
|
||||||
use crate::Ruma;
|
use crate::router::Ruma;
|
||||||
|
|
||||||
/// Recommended transferable state events list from the spec
|
/// Recommended transferable state events list from the spec
|
||||||
const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 9] = &[
|
const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 11] = &[
|
||||||
StateEventType::RoomAvatar,
|
StateEventType::RoomAvatar,
|
||||||
StateEventType::RoomEncryption,
|
StateEventType::RoomEncryption,
|
||||||
StateEventType::RoomGuestAccess,
|
StateEventType::RoomGuestAccess,
|
||||||
|
@ -34,6 +35,9 @@ const TRANSFERABLE_STATE_EVENTS: &[StateEventType; 9] = &[
|
||||||
StateEventType::RoomPowerLevels,
|
StateEventType::RoomPowerLevels,
|
||||||
StateEventType::RoomServerAcl,
|
StateEventType::RoomServerAcl,
|
||||||
StateEventType::RoomTopic,
|
StateEventType::RoomTopic,
|
||||||
|
// Not explicitly recommended in spec, but very useful.
|
||||||
|
StateEventType::SpaceChild,
|
||||||
|
StateEventType::SpaceParent, // TODO: m.room.policy?
|
||||||
];
|
];
|
||||||
|
|
||||||
/// # `POST /_matrix/client/r0/rooms/{roomId}/upgrade`
|
/// # `POST /_matrix/client/r0/rooms/{roomId}/upgrade`
|
||||||
|
@ -50,10 +54,7 @@ pub(crate) async fn upgrade_room_route(
|
||||||
State(services): State<crate::State>,
|
State(services): State<crate::State>,
|
||||||
body: Ruma<upgrade_room::v3::Request>,
|
body: Ruma<upgrade_room::v3::Request>,
|
||||||
) -> Result<upgrade_room::v3::Response> {
|
) -> Result<upgrade_room::v3::Response> {
|
||||||
debug_assert!(
|
// TODO[v12]: Handle additional creators
|
||||||
TRANSFERABLE_STATE_EVENTS.is_sorted(),
|
|
||||||
"TRANSFERABLE_STATE_EVENTS is not sorted"
|
|
||||||
);
|
|
||||||
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
|
||||||
|
|
||||||
if !services.server.supported_room_version(&body.new_version) {
|
if !services.server.supported_room_version(&body.new_version) {
|
||||||
|
@ -128,7 +129,7 @@ pub(crate) async fn upgrade_room_route(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
| _ => {
|
| _ => {
|
||||||
// "creator" key no longer exists in V11+ rooms
|
// "creator" key no longer exists in V11 rooms
|
||||||
create_event_content.remove("creator");
|
create_event_content.remove("creator");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -175,6 +176,7 @@ pub(crate) async fn upgrade_room_route(
|
||||||
&replacement_room,
|
&replacement_room,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
.boxed()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Join the new room
|
// Join the new room
|
||||||
|
@ -205,19 +207,30 @@ pub(crate) async fn upgrade_room_route(
|
||||||
&replacement_room,
|
&replacement_room,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
.boxed()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Replicate transferable state events to the new room
|
// Replicate transferable state events to the new room
|
||||||
for event_type in TRANSFERABLE_STATE_EVENTS {
|
for event_type in TRANSFERABLE_STATE_EVENTS {
|
||||||
|
let state_keys = services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.room_state_keys(&body.room_id, event_type)
|
||||||
|
.await?;
|
||||||
|
for state_key in state_keys {
|
||||||
let event_content = match services
|
let event_content = match services
|
||||||
.rooms
|
.rooms
|
||||||
.state_accessor
|
.state_accessor
|
||||||
.room_state_get(&body.room_id, event_type, "")
|
.room_state_get(&body.room_id, event_type, &state_key)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
| Ok(v) => v.content().to_owned(),
|
| Ok(v) => v.content().to_owned(),
|
||||||
| Err(_) => continue, // Skipping missing events.
|
| Err(_) => continue, // Skipping missing events.
|
||||||
};
|
};
|
||||||
|
if event_content.get() == "{}" {
|
||||||
|
// If the event content is empty, we skip it
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
services
|
services
|
||||||
.rooms
|
.rooms
|
||||||
|
@ -226,15 +239,17 @@ pub(crate) async fn upgrade_room_route(
|
||||||
PduBuilder {
|
PduBuilder {
|
||||||
event_type: event_type.to_string().into(),
|
event_type: event_type.to_string().into(),
|
||||||
content: event_content,
|
content: event_content,
|
||||||
state_key: Some(StateKey::new()),
|
state_key: Some(StateKey::from(state_key)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
sender_user,
|
sender_user,
|
||||||
&replacement_room,
|
&replacement_room,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
.boxed()
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Moves any local aliases to the new room
|
// Moves any local aliases to the new room
|
||||||
let mut local_aliases = services
|
let mut local_aliases = services
|
||||||
|
@ -290,10 +305,90 @@ pub(crate) async fn upgrade_room_route(
|
||||||
&body.room_id,
|
&body.room_id,
|
||||||
&state_lock,
|
&state_lock,
|
||||||
)
|
)
|
||||||
|
.boxed()
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
drop(state_lock);
|
drop(state_lock);
|
||||||
|
|
||||||
|
// Check if the old room has a space parent, and if so, whether we should update
|
||||||
|
// it (m.space.parent, room_id)
|
||||||
|
let parents = services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.room_state_keys(&body.room_id, &StateEventType::SpaceParent)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
for raw_space_id in parents {
|
||||||
|
let space_id = RoomId::parse(&raw_space_id)?;
|
||||||
|
let Ok(child) = services
|
||||||
|
.rooms
|
||||||
|
.state_accessor
|
||||||
|
.room_state_get_content::<SpaceChildEventContent>(
|
||||||
|
space_id,
|
||||||
|
&StateEventType::SpaceChild,
|
||||||
|
body.room_id.as_str(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
else {
|
||||||
|
// If the space does not have a child event for this room, we can skip it
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
debug!(
|
||||||
|
"Updating space {space_id} child event for room {} to {replacement_room}",
|
||||||
|
&body.room_id
|
||||||
|
);
|
||||||
|
// First, drop the space's child event
|
||||||
|
let state_lock = services.rooms.state.mutex.lock(space_id).await;
|
||||||
|
debug!("Removing space child event for room {} in space {space_id}", &body.room_id);
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.timeline
|
||||||
|
.build_and_append_pdu(
|
||||||
|
PduBuilder {
|
||||||
|
event_type: StateEventType::SpaceChild.into(),
|
||||||
|
content: to_raw_value(&RedactedSpaceChildEventContent {})
|
||||||
|
.expect("event is valid, we just created it"),
|
||||||
|
state_key: Some(body.room_id.clone().as_str().into()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
sender_user,
|
||||||
|
space_id,
|
||||||
|
&state_lock,
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
// Now, add a new child event for the replacement room
|
||||||
|
debug!("Adding space child event for room {replacement_room} in space {space_id}");
|
||||||
|
services
|
||||||
|
.rooms
|
||||||
|
.timeline
|
||||||
|
.build_and_append_pdu(
|
||||||
|
PduBuilder {
|
||||||
|
event_type: StateEventType::SpaceChild.into(),
|
||||||
|
content: to_raw_value(&SpaceChildEventContent {
|
||||||
|
via: vec![sender_user.server_name().to_owned()],
|
||||||
|
order: child.order,
|
||||||
|
suggested: child.suggested,
|
||||||
|
})
|
||||||
|
.expect("event is valid, we just created it"),
|
||||||
|
state_key: Some(replacement_room.as_str().into()),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
sender_user,
|
||||||
|
space_id,
|
||||||
|
&state_lock,
|
||||||
|
)
|
||||||
|
.boxed()
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
debug!(
|
||||||
|
"Finished updating space {space_id} child event for room {} to {replacement_room}",
|
||||||
|
&body.room_id
|
||||||
|
);
|
||||||
|
drop(state_lock);
|
||||||
|
}
|
||||||
|
|
||||||
// Return the replacement room id
|
// Return the replacement room id
|
||||||
Ok(upgrade_room::v3::Response { replacement_room })
|
Ok(upgrade_room::v3::Response { replacement_room })
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,3 +91,22 @@ pub async fn room_state_get(
|
||||||
.and_then(|shortstatehash| self.state_get(shortstatehash, event_type, state_key))
|
.and_then(|shortstatehash| self.state_get(shortstatehash, event_type, state_key))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns all state keys for the given `room_id` and `event_type`.
|
||||||
|
#[implement(super::Service)]
|
||||||
|
#[tracing::instrument(skip(self), level = "debug")]
|
||||||
|
pub async fn room_state_keys(
|
||||||
|
&self,
|
||||||
|
room_id: &RoomId,
|
||||||
|
event_type: &StateEventType,
|
||||||
|
) -> Result<Vec<String>> {
|
||||||
|
let shortstatehash = self.services.state.get_room_shortstatehash(room_id).await?;
|
||||||
|
|
||||||
|
let state_keys: Vec<String> = self
|
||||||
|
.state_keys(shortstatehash, event_type)
|
||||||
|
.map(|state_key| state_key.to_string())
|
||||||
|
.collect()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
Ok(state_keys)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue