fix and update

This commit is contained in:
lendry
2026-07-01 17:28:53 +03:00
parent 0c3c6d6d82
commit 06f1481787
16 changed files with 1502 additions and 11 deletions

View File

@@ -1,10 +1,10 @@
import { Body, Controller, ForbiddenException, Get, Param, Patch, Post, Query, UseGuards } from '@nestjs/common';
import { BadRequestException, Body, Controller, Delete, ForbiddenException, Get, Param, Patch, Post, Query, UseGuards } from '@nestjs/common';
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
import { firstValueFrom } from 'rxjs';
import { map } from 'rxjs';
import { CoreGrpcService } from '../core-grpc.service';
import { CurrentAdmin } from '../decorators/current-admin.decorator';
import { ListUsersQueryDto, ResetPasswordDto, SetSuperAdminDto, SetUserVerificationDto, UpdateUserDto } from '../dto/admin.dto';
import { ListUsersQueryDto, ResetPasswordDto, SetSuperAdminDto, SetUserVerificationDto, UpdateUserDto, UserInsightsQueryDto } from '../dto/admin.dto';
import { AdminGuard, AdminRequestUser, assertAdminPermission, SuperAdminGuard } from '../guards/admin.guard';
@ApiTags('Администрирование')
@@ -124,4 +124,112 @@ export class AdminController {
verificationIcon: dto.verificationIcon
});
}
@Get(':userId/sign-in-history')
@ApiOperation({ summary: 'История входов пользователя', description: 'Журнал SignInEvent с поиском по IP, устройству и причине.' })
getUserSignInHistory(@Param('userId') userId: string, @Query() query: UserInsightsQueryDto, @CurrentAdmin() admin: AdminRequestUser) {
if (!admin.canViewUsers && !admin.canManageUsers) {
throw new ForbiddenException('Недостаточно прав для просмотра журнала пользователя');
}
return firstValueFrom(
this.core.admin.GetUserSignInHistory({
userId,
search: query.search,
limit: query.limit,
offset: query.offset
})
);
}
@Get(':userId/activity')
@ApiOperation({ summary: 'Активность пользователя', description: 'Созданные документы, чаты, семьи, OAuth-согласия и журнал действий.' })
getUserActivity(@Param('userId') userId: string, @Query() query: UserInsightsQueryDto, @CurrentAdmin() admin: AdminRequestUser) {
if (!admin.canViewUsers && !admin.canManageUsers) {
throw new ForbiddenException('Недостаточно прав для просмотра активности пользователя');
}
return firstValueFrom(
this.core.admin.GetUserActivity({
userId,
search: query.search,
limit: query.limit,
offset: query.offset
})
);
}
@Get(':userId/chats')
@ApiOperation({ summary: 'Чаты пользователя для модерации', description: 'Обычные чаты без E2E и ботов.' })
listUserChats(@Param('userId') userId: string, @Query() query: UserInsightsQueryDto, @CurrentAdmin() admin: AdminRequestUser) {
if (!admin.canModerateChats && !admin.isSuperAdmin) {
throw new ForbiddenException('Недостаточно прав для модерации чатов');
}
return firstValueFrom(
this.core.admin.ListUserChatRooms({
userId,
search: query.search,
limit: query.limit,
offset: query.offset
})
);
}
@Get(':userId/chats/search')
@ApiOperation({ summary: 'Поиск по сообщениям пользователя', description: 'Поиск по обычным (не E2E) перепискам пользователя.' })
searchUserChats(@Param('userId') userId: string, @Query() query: UserInsightsQueryDto, @CurrentAdmin() admin: AdminRequestUser) {
if (!admin.canModerateChats && !admin.isSuperAdmin) {
throw new ForbiddenException('Недостаточно прав для модерации чатов');
}
if (!query.search?.trim()) {
throw new BadRequestException('Укажите параметр search');
}
return firstValueFrom(
this.core.admin.SearchUserChatMessages({
userId,
search: query.search.trim(),
limit: query.limit,
offset: query.offset
})
);
}
@Get(':userId/chats/:roomId/messages')
@ApiOperation({ summary: 'Сообщения чата пользователя', description: 'Просмотр переписки в обычном чате для модерации.' })
listUserChatMessages(
@Param('userId') userId: string,
@Param('roomId') roomId: string,
@Query() query: UserInsightsQueryDto,
@CurrentAdmin() admin: AdminRequestUser
) {
if (!admin.canModerateChats && !admin.isSuperAdmin) {
throw new ForbiddenException('Недостаточно прав для модерации чатов');
}
return firstValueFrom(
this.core.admin.ListUserChatMessages({
userId,
roomId,
search: query.search,
limit: query.limit,
beforeMessageId: query.beforeMessageId
})
);
}
@Delete(':userId/chat-messages/:messageId')
@ApiOperation({ summary: 'Удалить сообщение (модерация)', description: 'Мягкое удаление сообщения в обычном чате.' })
deleteUserChatMessage(
@Param('userId') userId: string,
@Param('messageId') messageId: string,
@CurrentAdmin() admin: AdminRequestUser
) {
if (!admin.canModerateChats && !admin.isSuperAdmin) {
throw new ForbiddenException('Недостаточно прав для модерации чатов');
}
void userId;
return firstValueFrom(
this.core.admin.AdminDeleteChatMessage({
actorUserId: admin.id,
messageId
})
);
}
}

View File

@@ -69,3 +69,23 @@ export class SetUserVerificationDto {
@IsString({ message: 'Значок должен быть строкой' })
verificationIcon?: string;
}
export class UserInsightsQueryDto {
@ApiPropertyOptional({ description: 'Поиск по событиям, IP, user-agent, тексту активности или чатам' })
@IsOptional()
@IsString({ message: 'Поисковая строка должна быть текстом' })
search?: string;
@ApiPropertyOptional({ description: 'Лимит записей', default: 50 })
@IsOptional()
limit?: number;
@ApiPropertyOptional({ description: 'Смещение для пагинации', default: 0 })
@IsOptional()
offset?: number;
@ApiPropertyOptional({ description: 'ID сообщения для пагинации истории чата' })
@IsOptional()
@IsString({ message: 'beforeMessageId должно быть строкой' })
beforeMessageId?: string;
}

View File

@@ -17,6 +17,7 @@ export interface AdminRequestUser {
canViewUsers: boolean;
canManageSettings: boolean;
canVerifyUsers: boolean;
canModerateChats: boolean;
roles: string[];
permissions: string[];
}
@@ -60,6 +61,7 @@ export class AdminGuard implements CanActivate {
canManageSettings: Boolean(profile.canManageSettings),
canViewUsers: Boolean(profile.canViewUsers),
canVerifyUsers: Boolean(profile.canVerifyUsers),
canModerateChats: Boolean(profile.canModerateChats),
roles: profile.roles ?? [],
permissions: profile.permissions ?? []
};