first commit
This commit is contained in:
53
apps/api-gateway/src/dto/addresses.dto.ts
Normal file
53
apps/api-gateway/src/dto/addresses.dto.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { IsIn, IsNumber, IsOptional, IsString, IsUUID, MinLength } from 'class-validator';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
export class UpsertAddressDto {
|
||||
@ApiPropertyOptional({ description: 'ID адреса для обновления' })
|
||||
@IsOptional()
|
||||
@IsUUID('4', { message: 'ID адреса должен быть UUID' })
|
||||
addressId?: string;
|
||||
|
||||
@ApiProperty({ description: 'Тип адреса', enum: ['HOME', 'WORK', 'OTHER'] })
|
||||
@IsIn(['HOME', 'WORK', 'OTHER'], { message: 'Некорректный тип адреса' })
|
||||
label!: 'HOME' | 'WORK' | 'OTHER';
|
||||
|
||||
@ApiProperty({ description: 'Город' })
|
||||
@IsString({ message: 'Город должен быть строкой' })
|
||||
@MinLength(1, { message: 'Укажите город' })
|
||||
city!: string;
|
||||
|
||||
@ApiProperty({ description: 'Улица' })
|
||||
@IsString({ message: 'Улица должна быть строкой' })
|
||||
@MinLength(1, { message: 'Укажите улицу' })
|
||||
street!: string;
|
||||
|
||||
@ApiProperty({ description: 'Дом' })
|
||||
@IsString({ message: 'Дом должен быть строкой' })
|
||||
@MinLength(1, { message: 'Укажите дом' })
|
||||
house!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Квартира или офис' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Квартира должна быть строкой' })
|
||||
apartment?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Комментарий' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Комментарий должен быть строкой' })
|
||||
comment?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Широта' })
|
||||
@IsOptional()
|
||||
@IsNumber({}, { message: 'Широта должна быть числом' })
|
||||
latitude?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Долгота' })
|
||||
@IsOptional()
|
||||
@IsNumber({}, { message: 'Долгота должна быть числом' })
|
||||
longitude?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Полный адрес одной строкой' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Полный адрес должен быть строкой' })
|
||||
fullAddress?: string;
|
||||
}
|
||||
60
apps/api-gateway/src/dto/admin.dto.ts
Normal file
60
apps/api-gateway/src/dto/admin.dto.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsEmail, IsEnum, IsOptional, IsString, Matches, MinLength } from 'class-validator';
|
||||
|
||||
export enum GatewayUserStatus {
|
||||
ACTIVE = 'ACTIVE',
|
||||
SUSPENDED = 'SUSPENDED',
|
||||
DELETED = 'DELETED'
|
||||
}
|
||||
|
||||
export class ListUsersQueryDto {
|
||||
@ApiPropertyOptional({ description: 'Поиск по почте, телефону, имени или логину' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Поисковая строка должна быть текстом' })
|
||||
search?: string;
|
||||
}
|
||||
|
||||
export class UpdateUserDto {
|
||||
@ApiPropertyOptional({ description: 'Отображаемое имя' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Имя должно быть строкой' })
|
||||
displayName?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Основная почта' })
|
||||
@IsOptional()
|
||||
@IsEmail({}, { message: 'Укажите корректную почту' })
|
||||
email?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Основной телефон' })
|
||||
@IsOptional()
|
||||
@Matches(/^\+?[1-9]\d{7,14}$/, { message: 'Укажите корректный телефон' })
|
||||
phone?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Резервная почта' })
|
||||
@IsOptional()
|
||||
@IsEmail({}, { message: 'Укажите корректную резервную почту' })
|
||||
backupEmail?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Резервный телефон' })
|
||||
@IsOptional()
|
||||
@Matches(/^\+?[1-9]\d{7,14}$/, { message: 'Укажите корректный резервный телефон' })
|
||||
backupPhone?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Статус пользователя', enum: GatewayUserStatus })
|
||||
@IsOptional()
|
||||
@IsEnum(GatewayUserStatus, { message: 'Укажите корректный статус пользователя' })
|
||||
status?: GatewayUserStatus;
|
||||
}
|
||||
|
||||
export class ResetPasswordDto {
|
||||
@ApiProperty({ description: 'Новый пароль пользователя', minLength: 8 })
|
||||
@IsString({ message: 'Пароль должен быть строкой' })
|
||||
@MinLength(8, { message: 'Пароль должен содержать минимум 8 символов' })
|
||||
newPassword!: string;
|
||||
}
|
||||
|
||||
export class SetSuperAdminDto {
|
||||
@ApiProperty({ description: 'Выдать или снять права супер-администратора' })
|
||||
@IsBoolean({ message: 'isSuperAdmin должно быть boolean' })
|
||||
isSuperAdmin!: boolean;
|
||||
}
|
||||
189
apps/api-gateway/src/dto/auth.dto.ts
Normal file
189
apps/api-gateway/src/dto/auth.dto.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsEmail, IsNotEmpty, IsOptional, IsString, Length, Matches, MinLength } from 'class-validator';
|
||||
|
||||
const EMAIL_OR_PHONE_PATTERN = /^([^\s@]+@[^\s@]+\.[^\s@]+|\+?[1-9]\d{9,14})$/;
|
||||
|
||||
export class RegisterDto {
|
||||
@ApiPropertyOptional({ description: 'Основная почта пользователя', example: 'user@example.com' })
|
||||
@IsOptional()
|
||||
@IsEmail({}, { message: 'Укажите корректную почту' })
|
||||
email?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Основной телефон пользователя', example: '+79990000000' })
|
||||
@IsOptional()
|
||||
@Matches(/^\+?[1-9]\d{7,14}$/, { message: 'Укажите корректный номер телефона' })
|
||||
phone?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Пароль пользователя. Не требуется для passwordless-регистрации.', minLength: 8 })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Пароль должен быть строкой' })
|
||||
@MinLength(8, { message: 'Пароль должен содержать минимум 8 символов' })
|
||||
password?: string;
|
||||
|
||||
@ApiProperty({ description: 'Имя, которое отображается в профиле', example: 'Иван Петров' })
|
||||
@IsString({ message: 'Имя должно быть строкой' })
|
||||
@IsNotEmpty({ message: 'Укажите имя профиля' })
|
||||
displayName!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Публичный логин пользователя', example: 'ivan' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Логин должен быть строкой' })
|
||||
username?: string;
|
||||
}
|
||||
|
||||
export class LoginDto {
|
||||
@ApiProperty({ description: 'Почта, телефон или логин', example: 'user@example.com' })
|
||||
@IsString({ message: 'Логин должен быть строкой' })
|
||||
@IsNotEmpty({ message: 'Укажите логин' })
|
||||
login!: string;
|
||||
|
||||
@ApiProperty({ description: 'Пароль пользователя' })
|
||||
@IsString({ message: 'Пароль должен быть строкой' })
|
||||
@IsNotEmpty({ message: 'Укажите пароль' })
|
||||
password!: string;
|
||||
|
||||
@ApiProperty({ description: 'Уникальный отпечаток устройства' })
|
||||
@IsString({ message: 'Отпечаток устройства должен быть строкой' })
|
||||
@IsNotEmpty({ message: 'Передайте отпечаток устройства' })
|
||||
fingerprint!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Название устройства', example: 'Chrome на Windows' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Название устройства должно быть строкой' })
|
||||
deviceName?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Тип устройства', example: 'WEB' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Тип устройства должен быть строкой' })
|
||||
deviceType?: string;
|
||||
}
|
||||
|
||||
export class IdentifyDto {
|
||||
@ApiProperty({ description: 'Почта или телефон пользователя', example: 'user@example.com' })
|
||||
@IsString({ message: 'Логин должен быть строкой' })
|
||||
@IsNotEmpty({ message: 'Укажите почту или телефон' })
|
||||
@Matches(EMAIL_OR_PHONE_PATTERN, { message: 'Укажите корректную почту или телефон в формате +79991234567' })
|
||||
login!: string;
|
||||
}
|
||||
|
||||
export class PasswordlessOtpDto {
|
||||
@ApiProperty({ description: 'Почта или телефон для входа', example: 'user@example.com' })
|
||||
@IsString({ message: 'Получатель должен быть строкой' })
|
||||
@IsNotEmpty({ message: 'Укажите почту или телефон' })
|
||||
@Matches(EMAIL_OR_PHONE_PATTERN, { message: 'Укажите корректную почту или телефон в формате +79991234567' })
|
||||
recipient!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Альтернативный канал доставки кода', enum: ['primary', 'email', 'phone', 'backupEmail', 'backupPhone'] })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Канал должен быть строкой' })
|
||||
channel?: string;
|
||||
}
|
||||
|
||||
export class PasswordlessVerifyDto {
|
||||
@ApiProperty({ description: 'Почта или телефон, на который отправлен код', example: 'user@example.com' })
|
||||
@IsString({ message: 'Получатель должен быть строкой' })
|
||||
@Matches(EMAIL_OR_PHONE_PATTERN, { message: 'Укажите корректную почту или телефон в формате +79991234567' })
|
||||
recipient!: string;
|
||||
|
||||
@ApiProperty({ description: 'OTP-код из 6 цифр', example: '123456' })
|
||||
@IsString({ message: 'Код должен быть строкой' })
|
||||
@Length(6, 6, { message: 'Код должен содержать 6 цифр' })
|
||||
@Matches(/^\d+$/, { message: 'Код должен содержать только цифры' })
|
||||
code!: string;
|
||||
|
||||
@ApiProperty({ description: 'Уникальный отпечаток устройства' })
|
||||
@IsString({ message: 'Отпечаток устройства должен быть строкой' })
|
||||
fingerprint!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Название устройства' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Название устройства должно быть строкой' })
|
||||
deviceName?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Тип устройства', example: 'WEB' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Тип устройства должен быть строкой' })
|
||||
deviceType?: string;
|
||||
}
|
||||
|
||||
export class PasswordLoginDto {
|
||||
@ApiPropertyOptional({ description: 'Почта или телефон пользователя для identifier-first входа' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Логин должен быть строкой' })
|
||||
@Matches(EMAIL_OR_PHONE_PATTERN, { message: 'Укажите корректную почту или телефон в формате +79991234567' })
|
||||
login?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Временный auth_token после успешной OTP-проверки, если используется legacy challenge flow' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Временный токен должен быть строкой' })
|
||||
tempAuthToken?: string;
|
||||
|
||||
@ApiProperty({ description: 'Пароль пользователя' })
|
||||
@IsString({ message: 'Пароль должен быть строкой' })
|
||||
@IsNotEmpty({ message: 'Укажите пароль' })
|
||||
password!: string;
|
||||
|
||||
@ApiProperty({ description: 'Уникальный отпечаток устройства' })
|
||||
@IsString({ message: 'Отпечаток устройства должен быть строкой' })
|
||||
fingerprint!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Название устройства' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Название устройства должно быть строкой' })
|
||||
deviceName?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Тип устройства', example: 'WEB' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Тип устройства должен быть строкой' })
|
||||
deviceType?: string;
|
||||
}
|
||||
|
||||
export class VerifyPinDto {
|
||||
@ApiProperty({ description: 'ID временно заблокированной сессии' })
|
||||
@IsString({ message: 'ID сессии должен быть строкой' })
|
||||
sessionId!: string;
|
||||
|
||||
@ApiProperty({ description: 'PIN-код из 4-6 цифр', example: '1234' })
|
||||
@IsString({ message: 'PIN-код должен быть строкой' })
|
||||
@Length(4, 6, { message: 'PIN-код должен содержать от 4 до 6 символов' })
|
||||
@Matches(/^\d+$/, { message: 'PIN-код должен содержать только цифры' })
|
||||
pin!: string;
|
||||
}
|
||||
|
||||
export class RefreshSessionDto {
|
||||
@ApiProperty({ description: 'Refresh token текущей сессии' })
|
||||
@IsString({ message: 'Refresh token должен быть строкой' })
|
||||
@IsNotEmpty({ message: 'Передайте refresh token' })
|
||||
refreshToken!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'ID сессии для ускоренной проверки' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'ID сессии должен быть строкой' })
|
||||
sessionId?: string;
|
||||
}
|
||||
|
||||
export class LdapLoginDto {
|
||||
@ApiProperty({ description: 'Логин LDAP (sAMAccountName, uid, mail и т.д.)', example: 'ivan.petrov' })
|
||||
@IsString({ message: 'Логин должен быть строкой' })
|
||||
@IsNotEmpty({ message: 'Укажите LDAP-логин' })
|
||||
username!: string;
|
||||
|
||||
@ApiProperty({ description: 'Пароль LDAP' })
|
||||
@IsString({ message: 'Пароль должен быть строкой' })
|
||||
@IsNotEmpty({ message: 'Укажите пароль' })
|
||||
password!: string;
|
||||
|
||||
@ApiProperty({ description: 'Уникальный отпечаток устройства' })
|
||||
@IsString({ message: 'Отпечаток устройства должен быть строкой' })
|
||||
fingerprint!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Название устройства' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Название устройства должно быть строкой' })
|
||||
deviceName?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Тип устройства', example: 'WEB' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Тип устройства должен быть строкой' })
|
||||
deviceType?: string;
|
||||
}
|
||||
68
apps/api-gateway/src/dto/chat.dto.ts
Normal file
68
apps/api-gateway/src/dto/chat.dto.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { IsArray, IsBoolean, IsIn, IsOptional, IsString, MinLength } from 'class-validator';
|
||||
|
||||
export class CreateChatRoomDto {
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
name!: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
memberUserIds?: string[];
|
||||
}
|
||||
|
||||
export class UpdateChatRoomDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
name?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
notificationsMuted?: boolean;
|
||||
}
|
||||
|
||||
export class SendChatMessageDto {
|
||||
@IsString()
|
||||
@IsIn(['TEXT', 'IMAGE', 'AUDIO', 'VOICE', 'FILE', 'EMOJI', 'POLL'])
|
||||
type!: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
content?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
replyToId?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
storageKey?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
mimeType?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
metadataJson?: string;
|
||||
|
||||
@IsOptional()
|
||||
poll?: {
|
||||
question: string;
|
||||
options: string[];
|
||||
allowsMultiple?: boolean;
|
||||
isAnonymous?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export class VotePollDto {
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
optionIds!: string[];
|
||||
}
|
||||
|
||||
export class SetRoomNotificationsMutedDto {
|
||||
@IsBoolean()
|
||||
muted!: boolean;
|
||||
}
|
||||
62
apps/api-gateway/src/dto/documents.dto.ts
Normal file
62
apps/api-gateway/src/dto/documents.dto.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsDateString, IsEnum, IsJSON, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export enum DocumentTypeDto {
|
||||
PASSPORT_RF = 'PASSPORT_RF',
|
||||
FOREIGN_PASSPORT = 'FOREIGN_PASSPORT',
|
||||
BIRTH_CERTIFICATE = 'BIRTH_CERTIFICATE',
|
||||
DRIVER_LICENSE = 'DRIVER_LICENSE',
|
||||
VEHICLE_REGISTRATION = 'VEHICLE_REGISTRATION',
|
||||
OMS = 'OMS',
|
||||
DMS = 'DMS',
|
||||
INN = 'INN',
|
||||
SNILS = 'SNILS'
|
||||
}
|
||||
|
||||
export class CreateDocumentDto {
|
||||
@ApiProperty({ description: 'Тип документа', enum: DocumentTypeDto, example: DocumentTypeDto.PASSPORT_RF })
|
||||
@IsEnum(DocumentTypeDto, { message: 'Некорректный тип документа' })
|
||||
type!: DocumentTypeDto;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Номер документа. Перед сохранением будет зашифрован.', example: '4512 123456' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Номер документа должен быть строкой' })
|
||||
number?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Дата выдачи документа в ISO формате', example: '2020-01-10T00:00:00.000Z' })
|
||||
@IsOptional()
|
||||
@IsDateString({}, { message: 'Дата выдачи должна быть в ISO формате' })
|
||||
issuedAt?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Дата окончания действия документа в ISO формате', example: '2030-01-10T00:00:00.000Z' })
|
||||
@IsOptional()
|
||||
@IsDateString({}, { message: 'Дата окончания должна быть в ISO формате' })
|
||||
expiresAt?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Данные формы документа в JSON. Перед сохранением будут зашифрованы.' })
|
||||
@IsOptional()
|
||||
@IsJSON({ message: 'Дополнительные данные должны быть валидным JSON' })
|
||||
metadataJson?: string;
|
||||
}
|
||||
|
||||
export class UpdateDocumentDto {
|
||||
@ApiPropertyOptional({ description: 'Номер документа' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Номер документа должен быть строкой' })
|
||||
number?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Дата выдачи в ISO формате' })
|
||||
@IsOptional()
|
||||
@IsDateString({}, { message: 'Дата выдачи должна быть в ISO формате' })
|
||||
issuedAt?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Дата окончания в ISO формате' })
|
||||
@IsOptional()
|
||||
@IsDateString({}, { message: 'Дата окончания должна быть в ISO формате' })
|
||||
expiresAt?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Данные формы документа в JSON' })
|
||||
@IsOptional()
|
||||
@IsJSON({ message: 'Дополнительные данные должны быть валидным JSON' })
|
||||
metadataJson?: string;
|
||||
}
|
||||
140
apps/api-gateway/src/dto/identity.dto.ts
Normal file
140
apps/api-gateway/src/dto/identity.dto.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsIn, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class OAuthAuthorizeQueryDto {
|
||||
@ApiProperty({ description: 'ID пользователя, который подтверждает OAuth доступ' })
|
||||
@IsString({ message: 'ID пользователя должен быть строкой' })
|
||||
userId!: string;
|
||||
|
||||
@ApiProperty({ description: 'OAuth client_id приложения' })
|
||||
@IsString({ message: 'client_id должен быть строкой' })
|
||||
clientId!: string;
|
||||
|
||||
@ApiProperty({ description: 'Разрешенный redirect_uri приложения' })
|
||||
@IsString({ message: 'redirect_uri должен быть строкой' })
|
||||
redirectUri!: string;
|
||||
|
||||
@ApiProperty({ description: 'Запрошенные scopes через пробел', example: 'openid profile email' })
|
||||
@IsString({ message: 'scope должен быть строкой' })
|
||||
scope!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'OAuth state для защиты клиента' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'state должен быть строкой' })
|
||||
state?: string;
|
||||
}
|
||||
|
||||
export class OAuthTokenDto {
|
||||
@ApiProperty({ description: 'Тип grant', enum: ['authorization_code', 'refresh_token'] })
|
||||
@IsIn(['authorization_code', 'refresh_token'], { message: 'grant_type должен быть authorization_code или refresh_token' })
|
||||
grantType!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Authorization code' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'code должен быть строкой' })
|
||||
code?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Refresh token' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'refresh_token должен быть строкой' })
|
||||
refreshToken?: string;
|
||||
|
||||
@ApiProperty({ description: 'OAuth client_id' })
|
||||
@IsString({ message: 'client_id должен быть строкой' })
|
||||
clientId!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'OAuth client_secret' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'client_secret должен быть строкой' })
|
||||
clientSecret?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'redirect_uri для authorization_code grant' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'redirect_uri должен быть строкой' })
|
||||
redirectUri?: string;
|
||||
}
|
||||
|
||||
export class SendOtpDto {
|
||||
@ApiProperty({ description: 'Почта или телефон получателя' })
|
||||
@IsString({ message: 'Получатель должен быть строкой' })
|
||||
target!: string;
|
||||
|
||||
@ApiProperty({ description: 'Канал доставки', enum: ['email', 'sms'] })
|
||||
@IsIn(['email', 'sms'], { message: 'Канал должен быть email или sms' })
|
||||
channel!: string;
|
||||
|
||||
@ApiProperty({ description: 'Назначение кода', example: 'login' })
|
||||
@IsString({ message: 'Назначение должно быть строкой' })
|
||||
purpose!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'ID пользователя, если код привязан к аккаунту' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'ID пользователя должен быть строкой' })
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
export class VerifyOtpDto {
|
||||
@ApiProperty({ description: 'Почта или телефон получателя' })
|
||||
@IsString({ message: 'Получатель должен быть строкой' })
|
||||
target!: string;
|
||||
|
||||
@ApiProperty({ description: 'Код подтверждения' })
|
||||
@IsString({ message: 'Код должен быть строкой' })
|
||||
code!: string;
|
||||
|
||||
@ApiProperty({ description: 'Назначение кода', example: 'login' })
|
||||
@IsString({ message: 'Назначение должно быть строкой' })
|
||||
purpose!: string;
|
||||
}
|
||||
|
||||
export class WebAuthnDto {
|
||||
@ApiProperty({ description: 'ID пользователя' })
|
||||
@IsString({ message: 'ID пользователя должен быть строкой' })
|
||||
userId!: string;
|
||||
}
|
||||
|
||||
export class QrSessionDto {
|
||||
@ApiProperty({ description: 'Название устройства', example: 'Chrome на Windows' })
|
||||
@IsString({ message: 'Название устройства должно быть строкой' })
|
||||
deviceName!: string;
|
||||
}
|
||||
|
||||
export class CreateFamilyGroupDto {
|
||||
@ApiProperty({ description: 'ID владельца семьи' })
|
||||
@IsString({ message: 'ID владельца должен быть строкой' })
|
||||
ownerId!: string;
|
||||
|
||||
@ApiProperty({ description: 'Название семейной группы', example: 'Семья Мамедовых' })
|
||||
@IsString({ message: 'Название семьи должно быть строкой' })
|
||||
@IsNotEmpty({ message: 'Укажите название семьи' })
|
||||
name!: string;
|
||||
}
|
||||
|
||||
export class UpdateFamilyGroupDto {
|
||||
@ApiProperty({ description: 'Новое название семейной группы' })
|
||||
@IsString({ message: 'Название семьи должно быть строкой' })
|
||||
name!: string;
|
||||
}
|
||||
|
||||
export class AddFamilyMemberDto {
|
||||
@ApiProperty({ description: 'ID пользователя, которого нужно добавить' })
|
||||
@IsString({ message: 'ID пользователя должен быть строкой' })
|
||||
userId!: string;
|
||||
|
||||
@ApiProperty({ description: 'Роль участника семьи', example: 'member' })
|
||||
@IsString({ message: 'Роль должна быть строкой' })
|
||||
role!: string;
|
||||
}
|
||||
|
||||
export class SendFamilyInviteDto {
|
||||
@ApiProperty({ description: 'Email, телефон или логин приглашаемого' })
|
||||
@IsString({ message: 'Укажите контакт приглашаемого' })
|
||||
@IsNotEmpty({ message: 'Укажите контакт приглашаемого' })
|
||||
target!: string;
|
||||
}
|
||||
|
||||
export class RespondFamilyInviteDto {
|
||||
@ApiProperty({ description: 'Принять приглашение' })
|
||||
@IsBoolean({ message: 'Укажите accept: true или false' })
|
||||
accept!: boolean;
|
||||
}
|
||||
40
apps/api-gateway/src/dto/media.dto.ts
Normal file
40
apps/api-gateway/src/dto/media.dto.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsIn, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
const IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'] as const;
|
||||
export class AvatarUploadDto {
|
||||
@ApiProperty({ description: 'MIME-тип изображения', example: 'image/jpeg', enum: IMAGE_TYPES })
|
||||
@IsString({ message: 'Укажите MIME-тип изображения' })
|
||||
@IsIn([...IMAGE_TYPES], { message: 'Допустимы только JPEG, PNG, WEBP или GIF' })
|
||||
contentType!: string;
|
||||
}
|
||||
|
||||
export class ConfirmAvatarDto {
|
||||
@ApiProperty({ description: 'Ключ объекта в MinIO после загрузки', example: 'avatars/uuid/photo.jpg' })
|
||||
@IsString({ message: 'Ключ хранилища должен быть строкой' })
|
||||
storageKey!: string;
|
||||
}
|
||||
|
||||
export class DocumentPhotoUploadDto {
|
||||
@ApiProperty({ description: 'MIME-тип изображения', example: 'image/jpeg', enum: IMAGE_TYPES })
|
||||
@IsString({ message: 'Укажите MIME-тип изображения' })
|
||||
@IsIn([...IMAGE_TYPES], { message: 'Допустимы только JPEG, PNG, WEBP или GIF' })
|
||||
contentType!: string;
|
||||
}
|
||||
|
||||
export class ChatMediaUploadDto {
|
||||
@ApiProperty({ description: 'MIME-тип файла', example: 'audio/webm' })
|
||||
@IsString({ message: 'Укажите MIME-тип файла' })
|
||||
contentType!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Имя файла для определения расширения', example: 'voice.webm' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Имя файла должно быть строкой' })
|
||||
fileName?: string;
|
||||
}
|
||||
|
||||
export class UpdateAvatarStorageDto {
|
||||
@ApiProperty({ description: 'Ключ объекта в MinIO', example: 'avatars/uuid/photo.jpg' })
|
||||
@IsString({ message: 'Ключ хранилища должен быть строкой' })
|
||||
storageKey!: string;
|
||||
}
|
||||
1
apps/api-gateway/src/dto/notifications.dto.ts
Normal file
1
apps/api-gateway/src/dto/notifications.dto.ts
Normal file
@@ -0,0 +1 @@
|
||||
export class MarkNotificationReadDto {}
|
||||
79
apps/api-gateway/src/dto/profile.dto.ts
Normal file
79
apps/api-gateway/src/dto/profile.dto.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsString, IsUrl, Matches, Max, Min, MinLength } from 'class-validator';
|
||||
|
||||
export class UpdateAvatarDto {
|
||||
@ApiPropertyOptional({ description: 'Публичная ссылка на аватар (legacy)', example: 'https://cdn.lendry.ru/avatars/user.png' })
|
||||
@IsOptional()
|
||||
@IsUrl({}, { message: 'Укажите корректную ссылку на аватар' })
|
||||
avatarUrl?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Ключ объекта в MinIO', example: 'avatars/uuid/photo.jpg' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Ключ хранилища должен быть строкой' })
|
||||
avatarStorageKey?: string;
|
||||
}
|
||||
|
||||
export class UpdateProfileDto {
|
||||
@ApiPropertyOptional({ description: 'Имя пользователя', example: 'Дмитрий' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Имя должно быть строкой' })
|
||||
firstName?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Фамилия пользователя', example: 'Мамедов' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Фамилия должна быть строкой' })
|
||||
lastName?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Краткое описание профиля', example: 'Разработчик и владелец аккаунта' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Описание должно быть строкой' })
|
||||
bio?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Возраст пользователя', minimum: 0, maximum: 130, example: 24 })
|
||||
@IsOptional()
|
||||
@IsInt({ message: 'Возраст должен быть целым числом' })
|
||||
@Min(0, { message: 'Возраст не может быть отрицательным' })
|
||||
@Max(130, { message: 'Возраст указан некорректно' })
|
||||
age?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Пол пользователя', example: 'male' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Пол должен быть строкой' })
|
||||
gender?: string;
|
||||
}
|
||||
|
||||
export class UpdateContactsDto {
|
||||
@ApiPropertyOptional({ description: 'Основная почта пользователя', example: 'user@example.com' })
|
||||
@IsOptional()
|
||||
@IsEmail({}, { message: 'Укажите корректную почту' })
|
||||
email?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Основной телефон пользователя', example: '+79990000000' })
|
||||
@IsOptional()
|
||||
@Matches(/^\+?[1-9]\d{7,14}$/, { message: 'Укажите корректный телефон' })
|
||||
phone?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Резервная почта пользователя', example: 'backup@example.com' })
|
||||
@IsOptional()
|
||||
@IsEmail({}, { message: 'Укажите корректную резервную почту' })
|
||||
backupEmail?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Резервный телефон пользователя', example: '+79991112233' })
|
||||
@IsOptional()
|
||||
@Matches(/^\+?[1-9]\d{7,14}$/, { message: 'Укажите корректный резервный телефон' })
|
||||
backupPhone?: string;
|
||||
}
|
||||
|
||||
export class SetPasswordDto {
|
||||
@ApiProperty({ description: 'Новый пароль для входа', minLength: 8 })
|
||||
@IsString({ message: 'Пароль должен быть строкой' })
|
||||
@MinLength(8, { message: 'Пароль должен содержать минимум 8 символов' })
|
||||
password!: string;
|
||||
}
|
||||
|
||||
export class UserIdParamDto {
|
||||
@ApiProperty({ description: 'ID пользователя' })
|
||||
@IsString({ message: 'ID пользователя должен быть строкой' })
|
||||
@IsNotEmpty({ message: 'Передайте ID пользователя' })
|
||||
userId!: string;
|
||||
}
|
||||
79
apps/api-gateway/src/dto/rbac.dto.ts
Normal file
79
apps/api-gateway/src/dto/rbac.dto.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsArray, IsBoolean, IsEnum, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export enum GatewayOAuthClientType {
|
||||
CONFIDENTIAL = 'CONFIDENTIAL',
|
||||
PUBLIC = 'PUBLIC'
|
||||
}
|
||||
|
||||
export class CreateRoleDto {
|
||||
@ApiProperty({ description: 'Уникальный slug роли', example: 'support' })
|
||||
@IsString({ message: 'Slug роли должен быть строкой' })
|
||||
slug!: string;
|
||||
|
||||
@ApiProperty({ description: 'Название роли', example: 'Поддержка' })
|
||||
@IsString({ message: 'Название роли должно быть строкой' })
|
||||
name!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Описание роли' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Описание должно быть строкой' })
|
||||
description?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Список slug прав', type: [String] })
|
||||
@IsOptional()
|
||||
@IsArray({ message: 'Права должны быть массивом' })
|
||||
@IsString({ each: true, message: 'Каждое право должно быть строкой' })
|
||||
permissionSlugs?: string[];
|
||||
}
|
||||
|
||||
export class AssignUserRoleDto {
|
||||
@ApiProperty({ description: 'Slug роли', example: 'admin' })
|
||||
@IsString({ message: 'Slug роли должен быть строкой' })
|
||||
roleSlug!: string;
|
||||
}
|
||||
|
||||
export class CreateOAuthClientDto {
|
||||
@ApiProperty({ description: 'Название приложения', example: 'Lendry Docs' })
|
||||
@IsString({ message: 'Название должно быть строкой' })
|
||||
name!: string;
|
||||
|
||||
@ApiProperty({ description: 'Redirect URI', type: [String], example: ['https://app.example.com/oauth/callback'] })
|
||||
@IsArray({ message: 'Redirect URI должны быть массивом' })
|
||||
@IsString({ each: true, message: 'Каждый redirect URI должен быть строкой' })
|
||||
redirectUris!: string[];
|
||||
|
||||
@ApiProperty({ description: 'Scopes', type: [String], example: ['openid', 'profile', 'email'] })
|
||||
@IsArray({ message: 'Scopes должны быть массивом' })
|
||||
@IsString({ each: true, message: 'Каждый scope должен быть строкой' })
|
||||
scopes!: string[];
|
||||
|
||||
@ApiPropertyOptional({ description: 'Тип клиента', enum: GatewayOAuthClientType })
|
||||
@IsOptional()
|
||||
@IsEnum(GatewayOAuthClientType, { message: 'Укажите корректный тип клиента' })
|
||||
type?: GatewayOAuthClientType;
|
||||
}
|
||||
|
||||
export class UpdateOAuthClientDto {
|
||||
@ApiPropertyOptional({ description: 'Название приложения' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Название должно быть строкой' })
|
||||
name?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Redirect URI', type: [String] })
|
||||
@IsOptional()
|
||||
@IsArray({ message: 'Redirect URI должны быть массивом' })
|
||||
@IsString({ each: true, message: 'Каждый redirect URI должен быть строкой' })
|
||||
redirectUris?: string[];
|
||||
|
||||
@ApiPropertyOptional({ description: 'Scopes', type: [String] })
|
||||
@IsOptional()
|
||||
@IsArray({ message: 'Scopes должны быть массивом' })
|
||||
@IsString({ each: true, message: 'Каждый scope должен быть строкой' })
|
||||
scopes?: string[];
|
||||
|
||||
@ApiPropertyOptional({ description: 'Активность приложения' })
|
||||
@IsOptional()
|
||||
@IsBoolean({ message: 'isActive должно быть boolean' })
|
||||
isActive?: boolean;
|
||||
}
|
||||
25
apps/api-gateway/src/dto/security.dto.ts
Normal file
25
apps/api-gateway/src/dto/security.dto.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsOptional, IsString, Length, Matches } from 'class-validator';
|
||||
|
||||
export class PinDto {
|
||||
@ApiProperty({ description: 'PIN-код из 4-6 цифр', example: '1234' })
|
||||
@IsString({ message: 'PIN-код должен быть строкой' })
|
||||
@Length(4, 6, { message: 'PIN-код должен содержать от 4 до 6 символов' })
|
||||
@Matches(/^\d+$/, { message: 'PIN-код должен содержать только цифры' })
|
||||
pin!: string;
|
||||
}
|
||||
|
||||
export class OptionalPinDto {
|
||||
@ApiPropertyOptional({ description: 'PIN-код для подтверждения действия' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'PIN-код должен быть строкой' })
|
||||
@Length(4, 6, { message: 'PIN-код должен содержать от 4 до 6 символов' })
|
||||
@Matches(/^\d+$/, { message: 'PIN-код должен содержать только цифры' })
|
||||
pin?: string;
|
||||
}
|
||||
|
||||
export class VerifySecurityPinDto extends PinDto {
|
||||
@ApiProperty({ description: 'ID сессии, которую нужно разблокировать PIN-кодом' })
|
||||
@IsString({ message: 'ID сессии должен быть строкой' })
|
||||
sessionId!: string;
|
||||
}
|
||||
69
apps/api-gateway/src/dto/settings.dto.ts
Normal file
69
apps/api-gateway/src/dto/settings.dto.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsBoolean, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class UpsertSettingDto {
|
||||
@ApiProperty({ description: 'Ключ настройки', example: 'PIN_LOCK_TIMEOUT_MINUTES' })
|
||||
@IsString({ message: 'Ключ настройки должен быть строкой' })
|
||||
@IsNotEmpty({ message: 'Укажите ключ настройки' })
|
||||
key!: string;
|
||||
|
||||
@ApiProperty({ description: 'Значение настройки', example: '15' })
|
||||
@IsString({ message: 'Значение настройки должно быть строкой' })
|
||||
value!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Описание настройки для администраторов' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Описание должно быть строкой' })
|
||||
description?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Скрывать ли значение в интерфейсе' })
|
||||
@IsOptional()
|
||||
@IsBoolean({ message: 'Признак секрета должен быть логическим значением' })
|
||||
isSecret?: boolean;
|
||||
}
|
||||
|
||||
export class UpsertSocialProviderDto {
|
||||
@ApiProperty({ description: 'Название OAuth провайдера', example: 'google' })
|
||||
@IsString({ message: 'Название провайдера должно быть строкой' })
|
||||
@IsNotEmpty({ message: 'Укажите провайдера' })
|
||||
providerName!: string;
|
||||
|
||||
@ApiProperty({ description: 'OAuth Client ID провайдера' })
|
||||
@IsString({ message: 'Client ID должен быть строкой' })
|
||||
@IsNotEmpty({ message: 'Укажите Client ID' })
|
||||
clientId!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'OAuth Client Secret провайдера' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Client Secret должен быть строкой' })
|
||||
clientSecret?: string;
|
||||
|
||||
@ApiProperty({ description: 'Включен ли провайдер для входа' })
|
||||
@IsBoolean({ message: 'Статус провайдера должен быть логическим значением' })
|
||||
isEnabled!: boolean;
|
||||
}
|
||||
|
||||
export class ConnectLinkedAccountDto {
|
||||
@ApiProperty({ description: 'Название провайдера', example: 'google' })
|
||||
@IsString({ message: 'Название провайдера должно быть строкой' })
|
||||
providerName!: string;
|
||||
|
||||
@ApiProperty({ description: 'ID пользователя у внешнего провайдера', example: 'google-user-123' })
|
||||
@IsString({ message: 'ID провайдера должен быть строкой' })
|
||||
providerId!: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Почта внешнего аккаунта' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Почта внешнего аккаунта должна быть строкой' })
|
||||
email?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Отображаемое имя внешнего аккаунта' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Имя внешнего аккаунта должно быть строкой' })
|
||||
displayName?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Аватар внешнего аккаунта' })
|
||||
@IsOptional()
|
||||
@IsString({ message: 'Аватар внешнего аккаунта должен быть строкой' })
|
||||
avatarUrl?: string;
|
||||
}
|
||||
Reference in New Issue
Block a user