From f20945c27e995519a811216b2941b61f53b8758e Mon Sep 17 00:00:00 2001 From: Innei Date: Thu, 10 Feb 2022 21:44:57 +0800 Subject: [PATCH] feat: terminal password support --- src/modules/configs/configs.service.ts | 11 ++--- src/modules/option/option.controller.ts | 2 +- .../gateway/admin/events.gateway.ts | 43 ++++++++++++++++++- src/processors/gateway/base.gateway.ts | 3 +- src/processors/gateway/events.types.ts | 1 + .../modules/configs/configs.service.spec.ts | 3 +- 6 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/modules/configs/configs.service.ts b/src/modules/configs/configs.service.ts index e388e65b..ac8a45b7 100644 --- a/src/modules/configs/configs.service.ts +++ b/src/modules/configs/configs.service.ts @@ -8,12 +8,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter' import { DocumentType, ReturnModelType } from '@typegoose/typegoose' import { BeAnObject } from '@typegoose/typegoose/lib/types' import camelcaseKeys from 'camelcase-keys' -import { - ClassConstructor, - instanceToPlain, - plainToClass, - plainToInstance, -} from 'class-transformer' +import { ClassConstructor, plainToInstance } from 'class-transformer' import { validateSync, ValidatorOptions } from 'class-validator' import cluster from 'cluster' import { cloneDeep, mergeWith } from 'lodash' @@ -145,7 +140,7 @@ export class ConfigsService { return new Promise((resolve, reject) => { this.waitForConfigReady() .then((config) => { - resolve(instanceToPlain(config[key]) as any) + resolve(config[key]) }) .catch(reject) }) @@ -250,7 +245,7 @@ export class ConfigsService { } private validWithDto(dto: ClassConstructor, value: any) { - const validModel = plainToClass(dto, value) + const validModel = plainToInstance(dto, value) const errors = validateSync(validModel, this.validOptions) if (errors.length > 0) { const error = this.validate.createExceptionFactory()(errors as any[]) diff --git a/src/modules/option/option.controller.ts b/src/modules/option/option.controller.ts index 9ef1e367..11de4638 100644 --- a/src/modules/option/option.controller.ts +++ b/src/modules/option/option.controller.ts @@ -49,7 +49,7 @@ export class OptionController { if (!value) { throw new BadRequestException('key is not exists.') } - return { data: value } + return { data: instanceToPlain(value) } } @Patch('/:key') diff --git a/src/processors/gateway/admin/events.gateway.ts b/src/processors/gateway/admin/events.gateway.ts index 07c03762..a59d8373 100644 --- a/src/processors/gateway/admin/events.gateway.ts +++ b/src/processors/gateway/admin/events.gateway.ts @@ -9,11 +9,13 @@ import { WebSocketServer, } from '@nestjs/websockets' import { Emitter } from '@socket.io/redis-emitter' +import { isNil } from 'lodash' import { IPty, spawn } from 'node-pty' import { resolve } from 'path' import SocketIO, { Socket } from 'socket.io' import { EventBusEvents } from '~/constants/event.constant' import { LOG_DIR } from '~/constants/path.constant' +import { ConfigsService } from '~/modules/configs/configs.service' import { CacheService } from '~/processors/cache/cache.service' import { getTodayLogFilePath } from '~/utils/consola.util' import { AuthService } from '../../../modules/auth/auth.service' @@ -29,6 +31,7 @@ export class AdminEventsGateway private readonly jwtService: JwtService, private readonly authService: AuthService, private readonly cacheService: CacheService, + private readonly configService: ConfigsService, ) { super() } @@ -118,7 +121,45 @@ export class AdminEventsGateway socket2ptyMap = new WeakMap() @SubscribeMessage('pty') - async pty(client: Socket, data?: { cols: number; rows: number }) { + async pty( + client: Socket, + data?: { password?: string; cols: number; rows: number }, + ) { + const password = data?.password + const terminalOptions = await this.configService.get('terminalOptions') + if (!terminalOptions.enable) { + client.send( + this.gatewayMessageFormat(EventTypes.PTY_MESSAGE, 'PTY 已禁用'), + ) + + return + } + + const isValidPassword = isNil(terminalOptions.password) + ? true + : password === terminalOptions.password + + if (!isValidPassword) { + if (typeof password === 'undefined' || password === '') { + client.send( + this.gatewayMessageFormat( + EventTypes.PTY_MESSAGE, + 'PTY 验证未通过:需要密码验证', + 10000, + ), + ) + } else { + client.send( + this.gatewayMessageFormat( + EventTypes.PTY_MESSAGE, + 'PTY 验证未通过:密码错误', + 10001, + ), + ) + } + + return + } const zsh = await nothrow($`zsh --version`) const pty = spawn( diff --git a/src/processors/gateway/base.gateway.ts b/src/processors/gateway/base.gateway.ts index 647819ab..2d815597 100644 --- a/src/processors/gateway/base.gateway.ts +++ b/src/processors/gateway/base.gateway.ts @@ -2,10 +2,11 @@ import { Socket } from 'socket.io' import { EventTypes } from './events.types' export abstract class BaseGateway { - public gatewayMessageFormat(type: EventTypes, message: any) { + public gatewayMessageFormat(type: EventTypes, message: any, code?: number) { return { type, data: message, + code, } } diff --git a/src/processors/gateway/events.types.ts b/src/processors/gateway/events.types.ts index a8354888..dc56aff0 100644 --- a/src/processors/gateway/events.types.ts +++ b/src/processors/gateway/events.types.ts @@ -42,6 +42,7 @@ export enum EventTypes { STDOUT = 'STDOUT', PTY = 'pty', + PTY_MESSAGE = 'pty_message', } export type NotificationTypes = 'error' | 'warn' | 'success' | 'info' diff --git a/test/src/modules/configs/configs.service.spec.ts b/test/src/modules/configs/configs.service.spec.ts index 8deebb06..8740d9fc 100644 --- a/test/src/modules/configs/configs.service.spec.ts +++ b/test/src/modules/configs/configs.service.spec.ts @@ -53,7 +53,8 @@ describe('Test ConfigsService', () => { const config = await service.getConfig() expect(config).toBeDefined() - expect(config).toStrictEqual(service.defaultConfig) + // use `toEqual` instead of `toStrictEqual` baseuse config is InstanceType of IConfig + expect(config).toEqual(service.defaultConfig) }) describe('patch config should apply change between db and redis', () => {