fix(realtime): события ролей, комнат и отзыв сессий без перезагрузки
Дефекты, найденные e2e-матрицей AGENT.md 11.6 (пункты 1, 2, 7, 12):
- ROLE_CREATE/ROLE_UPDATE уходили телом роли без guild_id, а клиент ждал
ссылку {guild_id, role_id} — событие отбрасывалось, роли у второго
участника не обновлялись. В rolePayload добавлено guild_id, parseRoleEvent
принимает и тело роли, и старую ссылку;
- выдача роли (MEMBER_UPDATE) и правки ролей не перечитывали список комнат,
поэтому приватная комната не появлялась в сайдбаре без F5 — теперь клиент
перечитывает комнаты по этим событиям;
- CHANNEL_CREATE/CHANNEL_UPDATE уходили телом комнаты с нулевым can_view
(права персональные), и клиент прятал комнату по этому признаку. Сервер
шлёт тело события без вычисленных прав плюс GUILD_CHANNELS_SYNC, а клиент
по событию комнаты перечитывает список;
- отзыв сессий (logout-all, смена пароля, админский сброс и «выйти везде»,
бан, удаление) не доходил до Gateway: второе устройство оставалось в
приложении. Добавлен gateway.InvalidateUser — клиент получает
INVALID_SESSION и закрывает соединение; клиент дополнительно обрабатывает
событие SESSION_INVALIDATED.
Тесты: gateway (отзыв сессий закрывает соединения и не задевает чужого),
web (событие SESSION_INVALIDATED, перечитывание комнат по CHANNEL_CREATE).
This commit is contained in:
@@ -60,7 +60,10 @@ func channelOverwritePayloads(overrides []store.ChannelOverride) []channelOverwr
|
||||
}
|
||||
|
||||
type rolePayload struct {
|
||||
ID string `json:"id"`
|
||||
ID string `json:"id"`
|
||||
// GuildID нужен событиям ROLE_CREATE/ROLE_UPDATE: в REST он избыточен
|
||||
// (сервер и так в пути), а в кадре шлюза без него роль некуда отнести.
|
||||
GuildID string `json:"guild_id,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Color int64 `json:"color"`
|
||||
Position int `json:"position"`
|
||||
@@ -110,6 +113,14 @@ type guildDetailPayload struct {
|
||||
Channels []channelPayload `json:"channels,omitempty"`
|
||||
}
|
||||
|
||||
// channelEventPayload собирает тело комнаты для события шлюза: права в нём
|
||||
// не вычислены (они персональные), поэтому признаки can_* не выставляются —
|
||||
// клиент по этому событию перечитывает список комнат, а не прячет комнату по
|
||||
// нулевому can_view.
|
||||
func channelEventPayload(channel store.Channel) channelPayload {
|
||||
return channelFromStore(channel, nil, nil)
|
||||
}
|
||||
|
||||
func channelFromStore(channel store.Channel, resolved *permissions.Resolved, overrides []store.ChannelOverride) channelPayload {
|
||||
payload := channelPayload{
|
||||
ID: formatSnowflake(channel.ID),
|
||||
@@ -141,6 +152,7 @@ func channelFromStore(channel store.Channel, resolved *permissions.Resolved, ove
|
||||
func roleFromStore(role store.Role) rolePayload {
|
||||
payload := rolePayload{
|
||||
ID: formatSnowflake(role.ID),
|
||||
GuildID: formatSnowflake(role.GuildID),
|
||||
Name: role.Name,
|
||||
Color: role.Color,
|
||||
Position: role.Position,
|
||||
@@ -585,7 +597,12 @@ func (s *Server) registerChannelRoutes(api huma.API) {
|
||||
"type": string(channel.Type),
|
||||
})
|
||||
s.invalidateGuild(guildID)
|
||||
s.dispatchGuildEvent(guildID, "CHANNEL_CREATE", channelFromStore(*channel, nil, nil))
|
||||
// Видимость комнаты считается персонально, поэтому вместо тела комнаты
|
||||
// (в нём can_view был бы нулевым) просим клиентов перечитать список.
|
||||
s.dispatchGuildEvent(guildID, "CHANNEL_CREATE", channelEventPayload(*channel))
|
||||
s.dispatchGuildEvent(guildID, "GUILD_CHANNELS_SYNC", map[string]any{
|
||||
"guild_id": formatSnowflake(guildID),
|
||||
})
|
||||
// Права на новую комнату считаем для автора запроса.
|
||||
resolved, err := s.perms.Channel(ctx, guildID, channel.ID, user.ID, user.IsInstanceAdmin)
|
||||
if err != nil {
|
||||
@@ -642,7 +659,10 @@ func (s *Server) registerChannelRoutes(api huma.API) {
|
||||
return nil, humaError(err)
|
||||
}
|
||||
s.invalidateGuild(guildID)
|
||||
s.dispatchGuildEvent(guildID, "CHANNEL_UPDATE", channelFromStore(*channel, nil, nil))
|
||||
s.dispatchGuildEvent(guildID, "CHANNEL_UPDATE", channelEventPayload(*channel))
|
||||
s.dispatchGuildEvent(guildID, "GUILD_CHANNELS_SYNC", map[string]any{
|
||||
"guild_id": formatSnowflake(guildID),
|
||||
})
|
||||
resolved, err := s.perms.Channel(ctx, guildID, channelID, user.ID, user.IsInstanceAdmin)
|
||||
if err != nil {
|
||||
return nil, humaError(err)
|
||||
@@ -822,6 +842,11 @@ func (s *Server) registerChannelRoutes(api huma.API) {
|
||||
"channel_id": formatSnowflake(channelID),
|
||||
"guild_id": formatSnowflake(guildID),
|
||||
})
|
||||
// Удалили комнату — у части участников могли остаться права на неё,
|
||||
// поэтому список перечитывают все (AGENT.md 11.6).
|
||||
s.dispatchGuildEvent(guildID, "GUILD_CHANNELS_SYNC", map[string]any{
|
||||
"guild_id": formatSnowflake(guildID),
|
||||
})
|
||||
return newOKOutput(), nil
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user