From 0dfb76d2cff37f5d7cc8db91846a1ddc855d8129 Mon Sep 17 00:00:00 2001 From: oneofthezombies Date: Mon, 13 Nov 2023 20:52:55 +0900 Subject: [PATCH] fix: device disconnection --- .../src/module/event/event.module.ts | 2 + .../src/module/event/live-session-updater.ts | 14 ++-- .../live-session/live-session.service.ts | 81 ++++++++++--------- .../private/private-device.controller.ts | 9 ++- 4 files changed, 60 insertions(+), 46 deletions(-) diff --git a/projects/console-web-server/src/module/event/event.module.ts b/projects/console-web-server/src/module/event/event.module.ts index 2fbe03f25..9e7679b4b 100644 --- a/projects/console-web-server/src/module/event/event.module.ts +++ b/projects/console-web-server/src/module/event/event.module.ts @@ -21,6 +21,7 @@ import { SlackModule } from '../../enterprise/module/integration/slack/slack.mod import { LicenseModule } from '../../enterprise/module/license/license.module'; import { DeviceMessageModule } from '../device-message/device-message.module'; import { LiveSessionModule } from '../live-session/live-session.module'; +import { DeviceModule } from '../organization/device/device.module'; import { ProjectModule } from '../project/project.module'; import { RedisModule } from '../redis/redis.module'; import { RemoteModule } from '../remote/remote.module'; @@ -75,6 +76,7 @@ import { UpdateProducer } from './update-producer'; ProjectModule, LiveSessionModule, RedisModule, + DeviceModule, ], providers: [ UpdateProducer, diff --git a/projects/console-web-server/src/module/event/live-session-updater.ts b/projects/console-web-server/src/module/event/live-session-updater.ts index e1862959a..204fa0083 100644 --- a/projects/console-web-server/src/module/event/live-session-updater.ts +++ b/projects/console-web-server/src/module/event/live-session-updater.ts @@ -7,6 +7,7 @@ import { config } from '../../config'; import { LiveSession } from '../../db/entity/live-session.entity'; import { LiveSessionService } from '../live-session/live-session.service'; import { DoguLogger } from '../logger/logger'; +import { DeviceCommandService } from '../organization/device/device-command.service'; import { RedisService } from '../redis/redis.service'; import { EventConsumer } from './event.consumer'; import { EventProducer } from './event.producer'; @@ -22,6 +23,7 @@ export class LiveSessionUpdater implements OnModuleInit, OnModuleDestroy { private readonly redis: RedisService, private readonly logger: DoguLogger, private readonly liveSessionService: LiveSessionService, + private readonly deviceCommandService: DeviceCommandService, ) { this.eventProducer = new EventProducer({ redis, @@ -29,8 +31,8 @@ export class LiveSessionUpdater implements OnModuleInit, OnModuleDestroy { key: config.redis.key.updateLiveSession, produceInterval: 1000, eventExpireTimeout: 60 * 1000, - onProduce: async () => { - return '0'; + onProduce: async (): Promise => { + return Promise.resolve('0'); }, }); this.eventConsumer = new EventConsumer({ @@ -38,16 +40,16 @@ export class LiveSessionUpdater implements OnModuleInit, OnModuleDestroy { logger, key: config.redis.key.updateLiveSession, consumeInterval: 1000, - onConsume: () => this.update(), + onConsume: async (): Promise => this.update(), }); } - onModuleInit() { + onModuleInit(): void { this.eventProducer.start(); this.eventConsumer.start(); } - onModuleDestroy() { + onModuleDestroy(): void { this.eventProducer.stop(); this.eventConsumer.stop(); } @@ -166,7 +168,7 @@ export class LiveSessionUpdater implements OnModuleInit, OnModuleDestroy { }); if (toCloses.length > 0) { - return await this.liveSessionService.closeInTransaction(manager, toCloses); + return await LiveSessionService.closeInTransaction(this.logger, this.deviceCommandService, manager, toCloses); } }); diff --git a/projects/console-web-server/src/module/live-session/live-session.service.ts b/projects/console-web-server/src/module/live-session/live-session.service.ts index 617b74766..d4dc84acd 100644 --- a/projects/console-web-server/src/module/live-session/live-session.service.ts +++ b/projects/console-web-server/src/module/live-session/live-session.service.ts @@ -1,5 +1,6 @@ import { CloudLicenseBase, DevicePropCamel, DeviceUsageState, LiveSessionCreateRequestBodyDto, LiveSessionFindQueryDto, OrganizationPropCamel } from '@dogu-private/console'; import { DeviceConnectionState, LiveSessionActiveStates, LiveSessionId, LiveSessionState, OrganizationId } from '@dogu-private/types'; +import { errorify } from '@dogu-tech/common'; import { HttpException, HttpStatus, Injectable, NotFoundException } from '@nestjs/common'; import { InjectDataSource } from '@nestjs/typeorm'; import { DataSource, EntityManager, In } from 'typeorm'; @@ -16,6 +17,46 @@ import { RedisService } from '../redis/redis.service'; @Injectable() export class LiveSessionService { + static updateLiveSessionToClosed(liveSession: LiveSession): LiveSession { + liveSession.state = LiveSessionState.CLOSED; + liveSession.closedAt = new Date(); + return liveSession; + } + + static async closeInTransaction(logger: DoguLogger, deviceCommandService: DeviceCommandService, manager: EntityManager, liveSessions: LiveSession[]): Promise { + const toCloseds = liveSessions.map((liveSession) => LiveSessionService.updateLiveSessionToClosed(liveSession)); + const closeds = await manager.getRepository(LiveSession).save(toCloseds); + + logger.debug('LiveSessionService.close.liveSessions', { + liveSessions, + }); + + const deviceIds = liveSessions.map((liveSession) => liveSession.deviceId); + const devices = await manager.getRepository(Device).find({ + where: { + deviceId: In(deviceIds), + }, + }); + devices.forEach((device) => { + device.usageState = DeviceUsageState.PREPARING; + }); + await manager.getRepository(Device).save(devices); + logger.debug('LiveSessionService.close.devices', { + devices, + }); + + devices.forEach((device) => { + deviceCommandService.reset(device.organizationId, device.deviceId, device.serial).catch((error) => { + logger.error('LiveSessionService.close.reset error', { + error: errorify(error), + device, + }); + }); + }); + + return closeds; + } + constructor( @InjectDataSource() private readonly dataSource: DataSource, @@ -128,42 +169,6 @@ export class LiveSessionService { /** * @description do NOT access this.dataSource in this method */ - async closeInTransaction(manager: EntityManager, liveSessions: LiveSession[]): Promise { - liveSessions.forEach((liveSession) => { - liveSession.state = LiveSessionState.CLOSED; - liveSession.closedAt = new Date(); - }); - const closeds = await manager.getRepository(LiveSession).save(liveSessions); - - this.logger.debug('LiveSessionService.close.liveSessions', { - liveSessions, - }); - - const deviceIds = liveSessions.map((liveSession) => liveSession.deviceId); - const devices = await manager.getRepository(Device).find({ - where: { - deviceId: In(deviceIds), - }, - }); - devices.forEach((device) => { - device.usageState = DeviceUsageState.PREPARING; - }); - await manager.getRepository(Device).save(devices); - this.logger.debug('LiveSessionService.close.devices', { - devices, - }); - - devices.forEach((device) => { - this.deviceCommandService.reset(device.organizationId, device.deviceId, device.serial).catch((error) => { - this.logger.error('LiveSessionService.close.reset error', { - error, - device, - }); - }); - }); - - return closeds; - } async closeByLiveSessionId(liveSessionId: LiveSessionId): Promise { const liveSession = await this.dataSource.getRepository(LiveSession).findOne({ where: { liveSessionId } }); @@ -177,7 +182,7 @@ export class LiveSessionService { } const closedSession = await this.dataSource.transaction(async (manager) => { - const rv = await this.closeInTransaction(manager, [liveSession]); + const rv = await LiveSessionService.closeInTransaction(this.logger, this.deviceCommandService, manager, [liveSession]); return rv[0]; }); @@ -192,7 +197,7 @@ export class LiveSessionService { }); await this.updateCloudLicenseId(liveSessionId, cloudLicenseId); await this.updateCloudLicenseLiveTestingHeartbeat(cloudLicenseId); - await this.cloudLicenseService.startUpdateLiveTesting(cloudLicenseId, { + this.cloudLicenseService.startUpdateLiveTesting(cloudLicenseId, { onOpen: async (close) => { await this.subscribeCloseEvent(liveSessionId, () => { close(); diff --git a/projects/console-web-server/src/module/private/private-device.controller.ts b/projects/console-web-server/src/module/private/private-device.controller.ts index 4f0ff3828..b23437b43 100644 --- a/projects/console-web-server/src/module/private/private-device.controller.ts +++ b/projects/console-web-server/src/module/private/private-device.controller.ts @@ -9,16 +9,18 @@ import { WriteDeviceRunTimeInfosRequestBody, } from '@dogu-private/console-host-agent'; import { FindDeviceBySerialQuery, UpdateDeviceRequestBody } from '@dogu-private/console-host-agent/src/http-specs/private-device'; -import { DeviceId, DEVICE_DISPLAY_ERROR_MAX_LENGTH, findDeviceModelNameByModelId, OrganizationId } from '@dogu-private/types'; +import { DeviceId, DEVICE_DISPLAY_ERROR_MAX_LENGTH, findDeviceModelNameByModelId, LiveSessionState, OrganizationId } from '@dogu-private/types'; import { Instance, transformAndValidate } from '@dogu-tech/common'; import { Body, ConflictException, Controller, Get, NotFoundException, Param, Patch, Post, Query } from '@nestjs/common'; import { InjectDataSource, InjectRepository } from '@nestjs/typeorm'; -import { DataSource, Repository } from 'typeorm'; +import { DataSource, Not, Repository } from 'typeorm'; import { Device, DEVICE_DEFAULT_MAX_PARALLEL_JOBS_IF_IS_HOST } from '../../db/entity/device.entity'; +import { LiveSession } from '../../db/entity/live-session.entity'; import { HOST_ACTION_TYPE } from '../auth/auth.types'; import { HostPermission } from '../auth/decorators'; import { DeviceMessageQueue } from '../device-message/device-message.queue'; import { InfluxDbDeviceService } from '../influxdb/influxdb-device.service'; +import { LiveSessionService } from '../live-session/live-session.service'; import { DoguLogger } from '../logger/logger'; import { DeviceStatusService } from '../organization/device/device-status.service'; import { IsDeviceExist } from '../organization/device/device.decorators'; @@ -134,6 +136,9 @@ export class PrivateDeviceController { ); await DeviceStatusService.updateDeviceBrowserInstallations(manager, deviceId, browserInstallations); await DeviceStatusService.updateDeviceRunners(manager, deviceId); + const liveSessions = await manager.getRepository(LiveSession).find({ where: { deviceId, state: Not(LiveSessionState.CLOSED) } }); + const toCloseLiveSessions = liveSessions.map((liveSession) => LiveSessionService.updateLiveSessionToClosed(liveSession)); + await manager.getRepository(LiveSession).save(toCloseLiveSessions); }); }