feat: terminal password support
This commit is contained in:
@@ -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<T extends object>(dto: ClassConstructor<T>, 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[])
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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<Socket, IPty>()
|
||||
|
||||
@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(
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ export enum EventTypes {
|
||||
STDOUT = 'STDOUT',
|
||||
|
||||
PTY = 'pty',
|
||||
PTY_MESSAGE = 'pty_message',
|
||||
}
|
||||
|
||||
export type NotificationTypes = 'error' | 'warn' | 'success' | 'info'
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
Reference in New Issue
Block a user