fix(server): правки сервера, ника и комнат попадают в аудит

§11.5 требует, чтобы каждое мутирующее действие писалось в журнал сервера
с `actor.instance_admin=true`, но у PATCH /guilds/{id},
PATCH /guilds/{id}/members/{user_id} и у ручек комнат вызова `recordAudit`
не было: переименование сервера, смена ника, тайм-аут и создание, правка и
удаление комнаты не оставляли следа ни у владельца, ни у инстанс-админа.

- guild.update, member.update, member.timeout (в причине — срок тайм-аута),
  channel.create, channel.update, channel.delete;
- Go-тест TestAuditCoversGuildMemberAndChannelChanges проверяет записи на
  чужом сервере и флаг actor_instance_admin, а также что действие владельца
  этим флагом не помечается;
- e2e 19b в instance-admin.spec.ts включён вместо test.fixme с диагностикой.

Проверки: go test -tags sqlite_fts5 -race -count=1 ./internal/... — ok;
make go-lint — 0 issues; живьём на стенде тест 19b проходит.
This commit is contained in:
2026-09-26 14:15:49 +03:00
parent 5a419112e0
commit 73dd51b121
3 changed files with 169 additions and 7 deletions
+16
View File
@@ -355,6 +355,9 @@ func (s *Server) registerGuildCoreRoutes(api huma.API) {
}
s.invalidateGuild(guildID)
s.dispatchGuildUpdate(*guild)
// Правка сервера попадает в журнал: раньше её не было ни у владельца,
// ни у инстанс-админа (AGENT.md 11.5).
s.recordAudit(ctx, user, guildID, "guild.update", "guild", &guildID, "")
return s.guildOutput(ctx, user, *guild)
})
@@ -596,6 +599,8 @@ func (s *Server) registerChannelRoutes(api huma.API) {
"name": channel.Name,
"type": string(channel.Type),
})
// Создание комнаты (в том числе категории) — в журнале сервера.
s.recordAudit(ctx, user, guildID, "channel.create", "channel", &channel.ID, "")
s.invalidateGuild(guildID)
// Видимость комнаты считается персонально, поэтому вместо тела комнаты
// (в нём can_view был бы нулевым) просим клиентов перечитать список.
@@ -659,6 +664,7 @@ func (s *Server) registerChannelRoutes(api huma.API) {
return nil, humaError(err)
}
s.invalidateGuild(guildID)
s.recordAudit(ctx, user, guildID, "channel.update", "channel", &channelID, "")
s.dispatchGuildEvent(guildID, "CHANNEL_UPDATE", channelEventPayload(*channel))
s.dispatchGuildEvent(guildID, "GUILD_CHANNELS_SYNC", map[string]any{
"guild_id": formatSnowflake(guildID),
@@ -838,6 +844,7 @@ func (s *Server) registerChannelRoutes(api huma.API) {
return nil, humaError(err)
}
s.invalidateGuild(guildID)
s.recordAudit(ctx, user, guildID, "channel.delete", "channel", &channelID, "")
s.dispatchGuildEvent(guildID, "CHANNEL_DELETE", map[string]any{
"channel_id": formatSnowflake(channelID),
"guild_id": formatSnowflake(guildID),
@@ -1108,6 +1115,15 @@ func (s *Server) registerMemberRoutes(api huma.API) {
return nil, humaError(err)
}
s.invalidateGuild(guildID)
// Ник и тайм-аут участника тоже действия модерации: журнал сервера
// должен показывать и их (AGENT.md 11.5).
if nickname != nil {
s.recordAudit(ctx, user, guildID, "member.update", "user", &targetID, "")
}
if timeoutUntil != nil {
s.recordAudit(ctx, user, guildID, "member.timeout", "user", &targetID,
"until "+timeoutUntil.UTC().Format(time.RFC3339))
}
payload := map[string]any{
"guild_id": formatSnowflake(guildID),
"user_id": formatSnowflake(targetID),