-- Recovery: orphan AppUser cleanup. -- -- Применяется один раз вручную на стейдже/проде после деплоя -- AuthorizationController + SuperAdminOrganizationsController фиксов -- (audit 2026-04-27 #1, #2, #7). -- -- Что делает: -- 1. Находит users у которых OrganizationId указывает на отсутствующую -- или архивированную организацию. -- 2. Деактивирует таких users (IsActive=false), сбрасывает OrganizationId. -- 3. Отзывает все OpenIddict refresh/access токены этих users -- (Status='revoked') чтобы существующие сессии оборвались. -- -- Идемпотентен: повторный запуск ничего не ломает. -- Не удаляет данные — только статусы. Юзер при необходимости может -- быть восстановлен ручным UPDATE users SET "IsActive"=true. BEGIN; WITH orphan_users AS ( SELECT u."Id" FROM users u LEFT JOIN organizations o ON o."Id" = u."OrganizationId" WHERE u."IsActive" = true AND ( u."OrganizationId" IS NULL OR o."Id" IS NULL OR o."IsArchived" = true ) AND NOT EXISTS ( -- Не трогаем SuperAdmin'ов — у них org=null это норма. SELECT 1 FROM "AspNetUserRoles" ur JOIN roles r ON r."Id" = ur."RoleId" WHERE ur."UserId" = u."Id" AND r."NormalizedName" = 'SUPERADMIN' ) ) UPDATE users SET "IsActive" = false, "OrganizationId" = NULL WHERE "Id" IN (SELECT "Id" FROM orphan_users); UPDATE "OpenIddictTokens" t SET "Status" = 'revoked' WHERE t."Status" = 'valid' AND t."Subject" IN ( SELECT u."Id"::text FROM users u WHERE u."IsActive" = false ); COMMIT;