fix(realtime): уход на вход после отзыва сессии и бан в открытом сервере

- invalidateCurrentUser перечитывает профиль, а не только удаляет кэш: без
  запроса AuthGuard не видит 401 и второе устройство остаётся в приложении
  после logout-all или смены пароля (AGENT.md 11.6, пункт 12);
- сброс сессии и уход из последнего сервера инвалидируют список серверов:
  пустой снапшот подставлял устаревший REST-ответ, и сервер возвращался в
  рейку (видно и после кика);
- joinGuild проверяет бан сервера: публичный сервер больше не обходится
  нажатием «Войти» вместо приглашения (AGENT.md 7.17, 7.20), тест расширен;
- e2e/voice-ten приведён к линту, console.log разрешён в e2e (диагностика
  прогона), артефакты Playwright исключены из prettier.
This commit is contained in:
2026-09-22 20:35:13 +03:00
parent b2a48d8008
commit 7ec9dbfdee
6 changed files with 55 additions and 48 deletions
+7
View File
@@ -1864,6 +1864,13 @@ func (s *Server) joinGuild(ctx context.Context, user *store.User, guildID uint64
if !guild.IsMain && !guild.IsDiscoverable && !user.IsInstanceAdmin {
return humaErrorStatus(http.StatusForbidden, "guild.invite_required", "this guild requires an invite")
}
// Бан сервера закрывает вход и в публичный сервер: иначе достаточно было бы
// нажать «Войти» вместо приглашения (AGENT.md 7.17, 7.20).
if banned, err := s.store.IsGuildBanned(ctx, guildID, user.ID); err != nil {
return humaError(err)
} else if banned {
return humaErrorStatus(http.StatusForbidden, "guild.banned", "you are banned from this guild")
}
settings, err := s.store.InstanceSettings(ctx)
if err != nil {
return humaError(err)
+10
View File
@@ -101,6 +101,16 @@ func TestBanMemberFlow(t *testing.T) {
t.Fatalf("код ошибки = %q", code)
}
// Открытый сервер (главный открыт всегда) тоже закрыт для забаненного:
// иначе бан обходится нажатием «Войти» вместо приглашения (AGENT.md 7.20).
byJoin := doJSON(t, srv, http.MethodPost, "/api/v1/guilds/"+guildID+"/join", "", member)
if byJoin.Code != http.StatusForbidden {
t.Fatalf("вход забаненного в публичный сервер = %d, ожидался 403", byJoin.Code)
}
if code := errorCodeOf(t, byJoin); code != "guild.banned" {
t.Fatalf("код ошибки публичного входа = %q", code)
}
// Снятие бана возвращает возможность войти.
unban := doJSON(t, srv, http.MethodDelete, "/api/v1/guilds/"+guildID+"/bans/"+memberID, "", owner)
if unban.Code != http.StatusOK {