feat: admin page proxy
This commit is contained in:
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -15,5 +15,8 @@
|
|||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.organizeImports": true
|
"source.organizeImports": true
|
||||||
},
|
},
|
||||||
"material-icon-theme.activeIconPack": "nest"
|
"material-icon-theme.activeIconPack": "nest",
|
||||||
|
"cSpell.words": [
|
||||||
|
"qaqdmin"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ type myError = {
|
|||||||
|
|
||||||
@Catch()
|
@Catch()
|
||||||
export class AllExceptionsFilter implements ExceptionFilter {
|
export class AllExceptionsFilter implements ExceptionFilter {
|
||||||
private readonly logger = new Logger('捕获异常')
|
private readonly logger = new Logger(AllExceptionsFilter.name)
|
||||||
private readonly errorLogPipe: WriteStream
|
private readonly errorLogPipe: WriteStream
|
||||||
constructor(@Inject(REFLECTOR) private reflector: Reflector) {
|
constructor(@Inject(REFLECTOR) private reflector: Reflector) {
|
||||||
this.errorLogPipe = fs.createWriteStream(resolve(LOGGER_DIR, 'error.log'), {
|
this.errorLogPipe = fs.createWriteStream(resolve(LOGGER_DIR, 'error.log'), {
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
import { Injectable, NestMiddleware } from '@nestjs/common'
|
import { Injectable, Logger, NestMiddleware } from '@nestjs/common'
|
||||||
import { IncomingMessage, ServerResponse } from 'http'
|
import { IncomingMessage, ServerResponse } from 'http'
|
||||||
import { parseRelativeUrl } from '~/utils/ip.util'
|
import { parseRelativeUrl } from '~/utils/ip.util'
|
||||||
// 用于屏蔽 PHP 的请求
|
// 用于屏蔽 PHP 的请求
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SecurityMiddleware implements NestMiddleware {
|
export class SecurityMiddleware implements NestMiddleware {
|
||||||
|
private logger: Logger
|
||||||
|
constructor() {
|
||||||
|
this.logger = new Logger(SecurityMiddleware.name)
|
||||||
|
}
|
||||||
async use(req: IncomingMessage, res: ServerResponse, next: () => void) {
|
async use(req: IncomingMessage, res: ServerResponse, next: () => void) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const url = parseRelativeUrl(req.originalUrl).pathname
|
const url = parseRelativeUrl(req.originalUrl).pathname
|
||||||
|
|||||||
@@ -155,4 +155,15 @@ export class AdminExtraDto {
|
|||||||
@IsString()
|
@IsString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
gaodemapKey?: string
|
gaodemapKey?: string
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
title?: string
|
||||||
|
|
||||||
|
@IsBoolean()
|
||||||
|
@IsOptional()
|
||||||
|
/**
|
||||||
|
* 是否开启后台反代访问
|
||||||
|
*/
|
||||||
|
enableAdminProxy?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,12 +51,13 @@ export class ConfigsService {
|
|||||||
this.logger = new Logger(ConfigsService.name)
|
this.logger = new Logger(ConfigsService.name)
|
||||||
}
|
}
|
||||||
private configInitd = false
|
private configInitd = false
|
||||||
|
|
||||||
public waitForConfigReady() {
|
public waitForConfigReady() {
|
||||||
// eslint-disable-next-line no-async-promise-executor
|
// eslint-disable-next-line no-async-promise-executor
|
||||||
return new Promise<IConfig>(async (r, j) => {
|
return new Promise<Readonly<IConfig>>(async (r, j) => {
|
||||||
// 开始等待, 后续调用直接返回
|
// 开始等待, 后续调用直接返回
|
||||||
if (this.configInitd) {
|
if (this.configInitd) {
|
||||||
r(this.config)
|
r(this.getConfig())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ export class ConfigsService {
|
|||||||
let curCount = 0
|
let curCount = 0
|
||||||
do {
|
do {
|
||||||
if (this.configInitd) {
|
if (this.configInitd) {
|
||||||
r({ ...this.config })
|
r(this.getConfig())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await sleep(100)
|
await sleep(100)
|
||||||
|
|||||||
@@ -1,14 +1,46 @@
|
|||||||
import { Controller, Get, Header } from '@nestjs/common'
|
import { Controller, Get, Header } from '@nestjs/common'
|
||||||
|
import { HttpCache } from '~/common/decorator/cache.decorator'
|
||||||
import { HTTPDecorators } from '~/common/decorator/http.decorator'
|
import { HTTPDecorators } from '~/common/decorator/http.decorator'
|
||||||
import { ApiName } from '~/common/decorator/openapi.decorator'
|
import { ApiName } from '~/common/decorator/openapi.decorator'
|
||||||
|
import { ConfigsService } from '../configs/configs.service'
|
||||||
|
|
||||||
@Controller()
|
interface IInjectableData {
|
||||||
|
BASE_API: null | string
|
||||||
|
WEB_URL: null | string
|
||||||
|
GATEWAY: null | string
|
||||||
|
LOGIN_BG: null | string
|
||||||
|
TITLE: null | string
|
||||||
|
}
|
||||||
|
|
||||||
|
@Controller('/')
|
||||||
@ApiName
|
@ApiName
|
||||||
export class PageProxyController {
|
export class PageProxyController {
|
||||||
@Get('/admin')
|
constructor(private readonly configs: ConfigsService) {}
|
||||||
|
|
||||||
|
@Get('/qaqdmin')
|
||||||
@Header('Content-Type', 'text/html')
|
@Header('Content-Type', 'text/html')
|
||||||
@HTTPDecorators.Bypass
|
@HTTPDecorators.Bypass
|
||||||
proxyAdmin() {
|
@HttpCache({ disable: true })
|
||||||
return ''
|
async proxyAdmin() {
|
||||||
|
const {
|
||||||
|
adminExtra,
|
||||||
|
url: { wsUrl, serverUrl, webUrl },
|
||||||
|
} = await this.configs.waitForConfigReady()
|
||||||
|
if (!adminExtra.enableAdminProxy) {
|
||||||
|
return '<h1>Admin Proxy is disabled</h1>'
|
||||||
|
}
|
||||||
|
const indexEntryUrl = `https://cdn.jsdelivr.net/gh/mx-space/admin-next@gh-pages/index.html`
|
||||||
|
let entry = await (await fetch(indexEntryUrl)).text()
|
||||||
|
entry = entry.replace(
|
||||||
|
`<!-- injectable script -->`,
|
||||||
|
`<script>${`window.injectData = ${JSON.stringify({
|
||||||
|
BASE_API: serverUrl,
|
||||||
|
GATEWAY: wsUrl,
|
||||||
|
LOGIN_BG: adminExtra.background,
|
||||||
|
TITLE: adminExtra.title,
|
||||||
|
WEB_URL: webUrl,
|
||||||
|
} as IInjectableData)}`}</script>`,
|
||||||
|
)
|
||||||
|
return entry
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user