136 lines
7.6 KiB
TypeScript
136 lines
7.6 KiB
TypeScript
import { Body, Controller, Delete, Get, Param, Post, Put, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common';
|
||
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||
import { map } from 'rxjs';
|
||
import { CoreGrpcService } from '../core-grpc.service';
|
||
import { CurrentAdmin } from '../decorators/current-admin.decorator';
|
||
import { ConnectLinkedAccountDto, TestMessagingDeliveryDto, UpsertSettingDto, UpsertSocialProviderDto } from '../dto/settings.dto';
|
||
import { AdminGuard, AdminRequestUser, assertAdminPermission } from '../guards/admin.guard';
|
||
|
||
const settingsWritePipe = new ValidationPipe({
|
||
whitelist: true,
|
||
transform: true,
|
||
forbidNonWhitelisted: false
|
||
});
|
||
|
||
@ApiTags('Глобальные настройки')
|
||
@ApiBearerAuth()
|
||
@UseGuards(AdminGuard)
|
||
@Controller('admin/settings')
|
||
export class SettingsController {
|
||
constructor(private readonly core: CoreGrpcService) {}
|
||
|
||
@Get()
|
||
@ApiOperation({ summary: 'Список системных настроек', description: 'Возвращает динамические настройки, включая PIN timeout и OAuth providers.' })
|
||
@ApiResponse({ status: 200, description: 'Список настроек получен' })
|
||
list(@CurrentAdmin() admin: AdminRequestUser) {
|
||
assertAdminPermission(admin, 'canManageSettings');
|
||
return this.core.settings.ListSettings({}).pipe(
|
||
map((response) => {
|
||
const payload = response as { settings?: unknown[] };
|
||
return { settings: payload.settings ?? [] };
|
||
})
|
||
);
|
||
}
|
||
|
||
@Get('key/:key')
|
||
@ApiOperation({ summary: 'Получить настройку', description: 'Возвращает SystemSetting по ключу.' })
|
||
@ApiParam({ name: 'key', description: 'Ключ настройки', example: 'PIN_LOCK_TIMEOUT_MINUTES' })
|
||
@ApiResponse({ status: 200, description: 'Настройка получена' })
|
||
get(@Param('key') key: string, @CurrentAdmin() admin: AdminRequestUser) {
|
||
assertAdminPermission(admin, 'canManageSettings');
|
||
return this.core.settings.GetSetting({ key });
|
||
}
|
||
|
||
@Put()
|
||
@UsePipes(settingsWritePipe)
|
||
@ApiOperation({ summary: 'Создать или обновить настройку', description: 'Меняет значение SystemSetting без изменения кода frontend.' })
|
||
@ApiBody({ type: UpsertSettingDto })
|
||
@ApiResponse({ status: 200, description: 'Настройка создана или обновлена' })
|
||
upsert(@Body() dto: UpsertSettingDto, @CurrentAdmin() admin: AdminRequestUser) {
|
||
assertAdminPermission(admin, 'canManageSettings');
|
||
return this.core.settings.UpsertSetting({
|
||
key: dto.key,
|
||
value: dto.value,
|
||
description: dto.description,
|
||
isSecret: dto.isSecret
|
||
});
|
||
}
|
||
|
||
@Delete('key/:key')
|
||
@ApiOperation({ summary: 'Удалить настройку', description: 'Удаляет SystemSetting по ключу.' })
|
||
@ApiParam({ name: 'key', description: 'Ключ настройки' })
|
||
@ApiResponse({ status: 200, description: 'Настройка удалена' })
|
||
delete(@Param('key') key: string, @CurrentAdmin() admin: AdminRequestUser) {
|
||
assertAdminPermission(admin, 'canManageSettings');
|
||
return this.core.settings.DeleteSetting({ key });
|
||
}
|
||
|
||
@Get('oauth/providers')
|
||
@ApiOperation({ summary: 'Список OAuth провайдеров', description: 'Возвращает глобальные настройки Google/Yandex и других social login providers.' })
|
||
@ApiResponse({ status: 200, description: 'Список провайдеров получен' })
|
||
listProviders(@CurrentAdmin() admin: AdminRequestUser) {
|
||
assertAdminPermission(admin, 'canManageSettings');
|
||
return this.core.settings.ListSocialProviders({});
|
||
}
|
||
|
||
@Put('oauth/providers')
|
||
@UsePipes(settingsWritePipe)
|
||
@ApiOperation({ summary: 'Создать или обновить OAuth провайдера', description: 'Управляет clientId/clientSecret и глобальным включением Google/Yandex входа.' })
|
||
@ApiBody({ type: UpsertSocialProviderDto })
|
||
@ApiResponse({ status: 200, description: 'Провайдер создан или обновлен' })
|
||
upsertProvider(@Body() dto: UpsertSocialProviderDto, @CurrentAdmin() admin: AdminRequestUser) {
|
||
assertAdminPermission(admin, 'canManageSettings');
|
||
return this.core.settings.UpsertSocialProvider({
|
||
providerName: dto.providerName,
|
||
clientId: dto.clientId,
|
||
clientSecret: dto.clientSecret,
|
||
isEnabled: dto.isEnabled
|
||
});
|
||
}
|
||
|
||
@Delete('oauth/providers/:providerName')
|
||
@ApiOperation({ summary: 'Удалить OAuth провайдера', description: 'Удаляет глобальную настройку social login provider.' })
|
||
@ApiParam({ name: 'providerName', description: 'Название провайдера', example: 'google' })
|
||
@ApiResponse({ status: 200, description: 'Провайдер удален' })
|
||
deleteProvider(@Param('providerName') providerName: string, @CurrentAdmin() admin: AdminRequestUser) {
|
||
assertAdminPermission(admin, 'canManageSettings');
|
||
return this.core.settings.DeleteSocialProvider({ providerName });
|
||
}
|
||
|
||
@Post('messaging/test')
|
||
@UsePipes(settingsWritePipe)
|
||
@ApiOperation({ summary: 'Тест email/SMS', description: 'Отправляет тестовый OTP-код через настроенного провайдера.' })
|
||
@ApiBody({ type: TestMessagingDeliveryDto })
|
||
@ApiResponse({ status: 200, description: 'Тестовое сообщение отправлено' })
|
||
testMessaging(@Body() dto: TestMessagingDeliveryDto, @CurrentAdmin() admin: AdminRequestUser) {
|
||
assertAdminPermission(admin, 'canManageSettings');
|
||
return this.core.settings.TestMessagingDelivery({ channel: dto.channel, target: dto.target });
|
||
}
|
||
|
||
@Get('linked-accounts/users/:userId')
|
||
@ApiOperation({ summary: 'Связанные внешние аккаунты', description: 'Возвращает LinkedAccount записи пользователя для Google/Yandex и других провайдеров.' })
|
||
@ApiParam({ name: 'userId', description: 'ID пользователя' })
|
||
@ApiResponse({ status: 200, description: 'Список внешних аккаунтов получен' })
|
||
listLinkedAccounts(@Param('userId') userId: string) {
|
||
return this.core.security.ListLinkedAccounts({ userId });
|
||
}
|
||
|
||
@Put('linked-accounts/users/:userId')
|
||
@ApiOperation({ summary: 'Подключить внешний аккаунт', description: 'Создает или обновляет LinkedAccount для social OAuth логина пользователя.' })
|
||
@ApiParam({ name: 'userId', description: 'ID пользователя' })
|
||
@ApiBody({ type: ConnectLinkedAccountDto })
|
||
@ApiResponse({ status: 200, description: 'Внешний аккаунт подключен' })
|
||
connectLinkedAccount(@Param('userId') userId: string, @Body() dto: ConnectLinkedAccountDto) {
|
||
return this.core.security.ConnectLinkedAccount({ userId, ...dto });
|
||
}
|
||
|
||
@Delete('linked-accounts/users/:userId/:providerName')
|
||
@ApiOperation({ summary: 'Отключить внешний аккаунт', description: 'Удаляет связь пользователя с Google/Yandex или другим social provider.' })
|
||
@ApiParam({ name: 'userId', description: 'ID пользователя' })
|
||
@ApiParam({ name: 'providerName', description: 'Название провайдера', example: 'google' })
|
||
@ApiResponse({ status: 200, description: 'Внешний аккаунт отключен' })
|
||
disconnectLinkedAccount(@Param('userId') userId: string, @Param('providerName') providerName: string) {
|
||
return this.core.security.DisconnectLinkedAccount({ userId, providerName });
|
||
}
|
||
}
|