05 admin user
AdminUserEndpoint — управление пользователями для операторов.
Все методы требуют JWT + запись в admin_user_roles. _requireAdmin проверяет сессию и наличие любой admin-роли. _requireRoles(allowed) дополнительно проверяет конкретные роли через JOIN admin_roles.
Методы¶
| Метод | Роль | Действие |
|---|---|---|
getUsers({page, pageSize, statusFilter?}) |
любой admin | Список пользователей с пагинацией, необязательный фильтр по status |
getDetail(userId) |
любой admin | Карточка: user + profile (маскированный) + kyc статус |
revealPii(userId, reason) |
operator, superadmin | Расшифровывает encryptedPii, пишет audit_log(pii_revealed) |
blockUser(userId, reason) |
superadmin, operator | Блокирует AuthUser + user.status=blocked + authenticationRevoked |
unblockUser(userId, reason) |
superadmin, operator | Разблокирует AuthUser, восстанавливает previousStatus из audit_log |
lockProfile(userId, reason) |
operator, superadmin | profile.isLocked=true; если user.status=pending_operator_review — вызывает KycService.finalizeVerification |
unlockProfile(userId, reason) |
operator, superadmin | profile.isLocked=false, очищает lockedAt |
updateProfile(userId, input) |
operator, superadmin | Корректирует PII через PiiProfileWriter; только если isLocked=false |
setEncryptPiiMode(userId, encryptPii, reason) |
operator, superadmin | Переключает режим хранения PII (открытый/зашифрованный) |
setNfcEnabled(userId, enabled, reason) |
operator, superadmin | Включает/выключает NFC для пользователя (gate C8) |
createWalletForUser(userId, currency) |
любой admin | Ручное провизионирование кошелька через WalletCreationService.ensureWallet (идемпотентно) |
createMerchant(email, firstName, lastName, businessName, taxId, businessCategoryCode, languageCode, ...) |
любой admin | Создаёт merchant + auth-аккаунт + PII + инвайт-письмо с tempPassword |
createAgent(email, firstName, lastName, businessName, taxId, languageCode, ...) |
любой admin | Создаёт agent + auth-аккаунт + PII + инвайт-письмо с tempPassword |
createConsumer(email, firstName, lastName, languageCode, ...) |
любой admin | Создаёт consumer + кошелёк THB (retry FutureCall если fail) + инвайт-письмо |
revealPii¶
- Обязательный
reason≥ 10 символов (UserApiException(invalid_reason)иначе). - Расшифровывает
encryptedPiiчерезPiiDecryptionServiceи возвращаетDecryptedProfile. - Поля в audit_log:
revealedFields(12 полей),reason,traceId. - Запись в
audit_logпишется всегда — даже если расшифровка не потребовалась.
blockUser / unblockUser¶
blockUser — три последовательных действия:
1. AuthUsers().update(blocked=true) — Serverpod отклонит refresh-токен.
2. session.messages.authenticationRevoked(authUserId) — активные streaming-соединения закрываются (best-effort, ошибка логируется).
3. User.db.updateById(status='blocked').
4. Audit: user_block с полем previousStatus (сохраняется для восстановления).
unblockUser — восстанавливает previousStatus:
1. Читает последний user_block audit_log.details → previousStatus.
2. Fallback если не найден: 'active'.
3. AuthUsers().update(blocked=false).
4. User.db.updateById(status=restoredStatus).
Создание пользователей (createMerchant / createAgent / createConsumer)¶
Все три метода работают в транзакции:
1. Проверка уникальности email.
2. AuthServices.instance.authUsers.create() — создаёт serverpod auth-запись.
3. User.db.insertRow(accountType=...).
4. EmailIdp.admin.createEmailAuthentication(tempPassword) — мерчант/агент/consumer может войти сразу.
5. PiiProfileWriter.upsert(...) — PII записывается / шифруется по encryptPii.
6. Дополнительные бизнес-поля (для merchant/agent: businessName, taxId).
После транзакции:
- Audit merchant_created / agent_created / consumer_created.
- RegistrationService.sendInviteEmail(tempPassword) — ошибка логируется, не ломает ответ.
- Только для consumer: WalletCreationService.ensureWallet(currency='THB') + FutureCall при ошибке.
Важно¶
- Merchant и agent создаются только через admin (не самостоятельная регистрация).
reason≥ 10 символов обязателен дляrevealPii,blockUser,unblockUser,lockProfile,unlockProfile,updateProfile,setEncryptPiiMode,setNfcEnabled.updateProfileработает только приprofile.isLocked=false. ПриisLocked=true→UserApiException(profile_locked).setNfcEnabled(false)— заморозка: метки сохраняются, NFC-операции отклоняются кодомNFC_DISABLED.