fix and update

This commit is contained in:
lendry
2026-06-29 22:51:25 +03:00
parent 885b07d76b
commit 57cb58347b
30 changed files with 799 additions and 187 deletions

View File

@@ -414,6 +414,7 @@ export class AuthGrpcController {
reason: event.reason,
ipAddress: event.ipAddress,
userAgent: event.userAgent,
deviceName: event.device?.name ?? null,
createdAt: event.createdAt.toISOString()
}))
};
@@ -1235,7 +1236,19 @@ export class AuthGrpcController {
return this.botApi.listBotChatMessages(command.userId, command.botRef, command.roomId);
}
private toSessionResponse(session: { id: string; userId: string; deviceId: string | null; pinVerified: boolean; status: string; ipAddress: string | null; userAgent: string | null; expiresAt: Date; createdAt: Date; updatedAt: Date }) {
private toSessionResponse(session: {
id: string;
userId: string;
deviceId: string | null;
pinVerified: boolean;
status: string;
ipAddress: string | null;
userAgent: string | null;
expiresAt: Date;
createdAt: Date;
updatedAt: Date;
device?: { name: string; type: string } | null;
}) {
return {
id: session.id,
userId: session.userId,
@@ -1244,6 +1257,8 @@ export class AuthGrpcController {
status: session.status,
ipAddress: session.ipAddress,
userAgent: session.userAgent,
deviceName: session.device?.name ?? null,
deviceType: session.device?.type ?? null,
expiresAt: session.expiresAt.toISOString(),
createdAt: session.createdAt.toISOString(),
updatedAt: session.updatedAt.toISOString()

View File

@@ -14,6 +14,7 @@ import { SettingsService } from './settings.service';
import { MessagingService } from '../infra/messaging.service';
import { NotificationsService } from './notifications.service';
import { canonicalPhoneE164, phoneLookupVariants } from '../infra/phone.util';
import { resolveDeviceName } from '../infra/client-device.util';
import { TotpService } from './totp.service';
import { RbacService } from './rbac.service';
import { DEFAULT_VERIFICATION_ICON, resolveVerificationIcon } from './verification.constants';
@@ -289,6 +290,33 @@ export class AuthService {
);
}
private async publishLoginSuccessNotification(
userId: string,
deviceId: string,
ipAddress?: string | null,
userAgent?: string | null
) {
try {
const device = await this.prisma.device.findUnique({ where: { id: deviceId } });
const deviceLabel = device?.name ?? resolveDeviceName(undefined, userAgent);
const ipLabel = ipAddress?.trim() || 'неизвестный IP';
await this.notifications.create(
userId,
'login_success',
'Выполнен вход',
`Выполнен вход с устройства «${deviceLabel}» и IP ${ipLabel}`,
{
deviceId,
deviceName: deviceLabel,
ipAddress: ipAddress ?? null,
userAgent: userAgent ?? null
}
);
} catch {
// Уведомление о входе не должно блокировать авторизацию.
}
}
async verifyOtp(command: LoginCommand & { recipient: string; code: string }) {
const existingUser = await this.findUserByRecipient(command.recipient);
const targetVariants = this.recipientLookupTargets(command.recipient);
@@ -431,7 +459,8 @@ export class AuthService {
fingerprint: command.fingerprint,
deviceName: command.deviceName,
deviceType: command.deviceType,
ipAddress: command.ipAddress
ipAddress: command.ipAddress,
userAgent: command.userAgent
});
return this.finalizeAuthentication(
user,
@@ -649,20 +678,21 @@ export class AuthService {
return `totp:login:attempts:${challengeId}`;
}
private async upsertDevice(userId: string, command: Pick<LoginCommand, 'fingerprint' | 'deviceName' | 'deviceType' | 'ipAddress'>) {
private async upsertDevice(userId: string, command: Pick<LoginCommand, 'fingerprint' | 'deviceName' | 'deviceType' | 'ipAddress' | 'userAgent'>) {
const resolvedName = resolveDeviceName(command.deviceName, command.userAgent);
return this.prisma.device.upsert({
where: { userId_fingerprint: { userId, fingerprint: command.fingerprint } },
create: {
userId,
fingerprint: command.fingerprint,
name: command.deviceName ?? 'Новое устройство',
name: resolvedName,
type: this.toDeviceType(command.deviceType),
lastIp: command.ipAddress
},
update: {
lastSeenAt: new Date(),
lastIp: command.ipAddress,
name: command.deviceName ?? undefined,
name: resolvedName,
type: this.toDeviceType(command.deviceType)
}
});
@@ -688,6 +718,7 @@ export class AuthService {
});
await this.notifications.dismissOtpCodeNotifications(user.id);
await this.publishLoginSuccessNotification(user.id, deviceId, command.ipAddress, command.userAgent);
return {
accessToken: await this.issueAccessToken(user, session.id, pinVerified),