feat: pageproxy debug mode

This commit is contained in:
Innei
2021-09-20 14:48:02 +08:00
parent d6579941ce
commit 8b7532dc3d
6 changed files with 145 additions and 15 deletions

View File

@@ -73,6 +73,7 @@
"dotenv": "*",
"ejs": "3.1.6",
"fastify-multipart": "5.0.0",
"fastify-secure-session": "^2.3.1",
"fastify-swagger": "4.12.0",
"graphql": "15.5.3",
"html-minifier": "4.0.0",

41
pnpm-lock.yaml generated
View File

@@ -58,6 +58,7 @@ specifiers:
eslint: '*'
fastify: '*'
fastify-multipart: 5.0.0
fastify-secure-session: ^2.3.1
fastify-swagger: 4.12.0
graphql: 15.5.3
html-minifier: 4.0.0
@@ -137,6 +138,7 @@ dependencies:
dotenv: 10.0.0
ejs: 3.1.6
fastify-multipart: 5.0.0
fastify-secure-session: 2.3.1
fastify-swagger: 4.12.0
graphql: 15.5.3
html-minifier: 4.0.0
@@ -3390,6 +3392,11 @@ packages:
safe-buffer: 5.1.2
dev: true
/cookie-signature/1.1.0:
resolution: {integrity: sha512-Alvs19Vgq07eunykd3Xy2jF0/qSNv2u7KDbAek9H5liV1UMijbqFs5cycZvv5dVsvseT/U4H8/7/w8Koh35C4A==}
engines: {node: '>=6.6.0'}
dev: false
/cookie/0.4.1:
resolution: {integrity: sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==}
engines: {node: '>= 0.6'}
@@ -4169,6 +4176,14 @@ packages:
fastify-plugin: 2.3.4
dev: false
/fastify-cookie/5.3.1:
resolution: {integrity: sha512-ubiC5ydvgqaXOydeg3G+SFEiTJtsyNPde6ForXi+UG66aDbsHlWNLJTQZg4BopXv+MJ0SzlShGVCWv26mgAwpw==}
dependencies:
cookie: 0.4.1
cookie-signature: 1.1.0
fastify-plugin: 3.0.0
dev: false
/fastify-cors/6.0.2:
resolution: {integrity: sha512-sE0AOyzmj5hLLRRVgenjA6G2iOGX35/1S3QGYB9rr9TXelMZB3lFrXy4CzwYVOMiujJeMiLgO4J7eRm8sQSv8Q==}
dependencies:
@@ -4207,6 +4222,15 @@ packages:
resolution: {integrity: sha512-ZdCvKEEd92DNLps5n0v231Bha8bkz1DjnPP/aEz37rz/q42Z5JVLmgnqR4DYuNn3NXAO3IDCPyRvgvxtJ4Ym4w==}
dev: false
/fastify-secure-session/2.3.1:
resolution: {integrity: sha512-6XsatyRSiX0UQB0MOPlU/PC9yn3seImefS1yv8C0bAyjAJ876839eHKPrclwNKBVX+9SUX+LdJGJTBi8DSU63g==}
hasBin: true
dependencies:
fastify-cookie: 5.3.1
fastify-plugin: 3.0.0
sodium-native: 3.2.1
dev: false
/fastify-static/4.2.3:
resolution: {integrity: sha512-uFRgwYXZwLKyaMrByf10efO+HTjAPqyQOlUthoGljQKGCfbwUeTeE7EHadsDWeN7NMeqBE617RamVh9uqatuUw==}
dependencies:
@@ -4836,6 +4860,10 @@ packages:
/inherits/2.0.4:
resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
/ini/1.3.8:
resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
dev: false
/inquirer/7.3.3:
resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==}
engines: {node: '>=8.0.0'}
@@ -6447,6 +6475,11 @@ packages:
engines: {node: 4.x || >=6.0.0}
dev: false
/node-gyp-build/4.3.0:
resolution: {integrity: sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==}
hasBin: true
dev: false
/node-int64/0.4.0:
resolution: {integrity: sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=}
dev: true
@@ -7581,6 +7614,14 @@ packages:
- supports-color
- utf-8-validate
/sodium-native/3.2.1:
resolution: {integrity: sha512-EgDZ/Z7PxL2kCasKk7wnRkV8W9kvwuIlHuHXAxkQm3FF0MgVsjyLBXGjSRGhjE6u7rhSpk3KaMfFM23bfMysIQ==}
requiresBuild: true
dependencies:
ini: 1.3.8
node-gyp-build: 4.3.0
dev: false
/sonic-boom/1.4.1:
resolution: {integrity: sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==}
dependencies:

View File

@@ -1,6 +1,6 @@
import type { AxiosRequestConfig } from 'axios'
import { argv } from 'yargs'
import yargs from 'yargs'
const argv = yargs.argv as any
console.log(argv)
export const API_VERSION = 2
@@ -47,4 +47,9 @@ export const SECURITY = {
jwtExpire: '7d',
// 跳过登陆鉴权
skipAuth: argv.skipAuth ?? false,
get secret() {
return this.jwtSecret
},
// 必须 16 位
salt: argv.salt || 'axczswrasxzfqxsa',
}

View File

@@ -1,10 +1,14 @@
import { FastifyAdapter } from '@nestjs/platform-fastify'
import FastifyMultipart from 'fastify-multipart'
export const fastifyApp: FastifyAdapter = new FastifyAdapter({
import secureSession from 'fastify-secure-session'
import { SECURITY } from '~/app.config'
const app: FastifyAdapter = new FastifyAdapter({
trustProxy: true,
})
export { app as fastifyApp }
fastifyApp.register(FastifyMultipart, {
app.register(FastifyMultipart, {
limits: {
fields: 10, // Max number of non-file fields
fileSize: 1024 * 1024 * 6, // limit size 6M
@@ -12,7 +16,7 @@ fastifyApp.register(FastifyMultipart, {
},
})
fastifyApp.getInstance().addHook('onRequest', (request, reply, done) => {
app.getInstance().addHook('onRequest', (request, reply, done) => {
const origin = request.headers.origin
if (!origin) {
request.headers.origin = request.headers.host
@@ -20,3 +24,12 @@ fastifyApp.getInstance().addHook('onRequest', (request, reply, done) => {
done()
})
app.register(secureSession, {
secret: SECURITY.secret.slice(10).repeat(4),
salt: SECURITY.salt,
cookie: {
path: '/',
httpOnly: true,
},
})

View File

@@ -1,4 +1,5 @@
import { Controller, Get, Header } from '@nestjs/common'
import { Controller, Get, Header, Query, Session } from '@nestjs/common'
import * as secureSession from 'fastify-secure-session'
import { API_VERSION } from '~/app.config'
import { HTTPDecorators } from '~/common/decorator/http.decorator'
import { ApiName } from '~/common/decorator/openapi.decorator'
@@ -7,6 +8,7 @@ import { CacheService } from '~/processors/cache/cache.service'
import { getRedisKey } from '~/utils/redis.util'
import { ConfigsService } from '../configs/configs.service'
import { InitService } from '../init/init.service'
import { PageProxyDebugDto } from './pageproxy.dto'
interface IInjectableData {
BASE_API: null | string
@@ -30,7 +32,10 @@ export class PageProxyController {
@Get('/qaqdmin')
@Header('Content-Type', 'text/html')
@HTTPDecorators.Bypass
async proxyAdmin() {
async proxyAdmin(
@Session() session: secureSession.Session,
@Query() query: PageProxyDebugDto,
) {
const {
adminExtra,
url: { webUrl },
@@ -38,24 +43,57 @@ export class PageProxyController {
if (!adminExtra.enableAdminProxy && !isDev) {
return '<h1>Admin Proxy is disabled</h1>'
}
const {
__apiUrl: apiUrl,
__gatewayUrl: gatewayUrl,
__onlyGithub: onlyGithub,
__debug: debug,
} = query
session.options({ maxAge: 1000 * 60 * 10 })
if (apiUrl) {
session.set('__apiUrl', apiUrl)
}
if (gatewayUrl) {
session.set('__gatewayUrl', gatewayUrl)
}
if (debug === false) {
session.delete()
}
let entry =
(await this.cacheService.get<string>(getRedisKey(RedisKeys.AdminPage))) ||
(!onlyGithub &&
(await this.cacheService.get<string>(
getRedisKey(RedisKeys.AdminPage),
))) ||
(await (async () => {
const indexEntryUrl = `https://raw.githubusercontent.com/mx-space/admin-next/gh-pages/index.html`
const indexEntryCdnUrl = `https://cdn.jsdelivr.net/gh/mx-space/admin-next@gh-pages/index.html?t=${+new Date()}`
return await Promise.any([
const tasks = [
// 龟兔赛跑, 乌龟先跑
// eslint-disable-next-line @typescript-eslint/no-empty-function
fetch(indexEntryUrl).then((res) => res.text()),
sleep(1000).then(async () => (await fetch(indexEntryCdnUrl)).text()),
])
]
if (!onlyGithub) {
tasks.push(
sleep(1000).then(async () =>
(await fetch(indexEntryCdnUrl)).text(),
),
)
}
return await Promise.any(tasks)
})())
await this.cacheService.set(getRedisKey(RedisKeys.AdminPage), entry, {
ttl: 10 * 60,
})
const sessionInjectableData = {
BASE_API: session.get('__apiUrl'),
GATEWAY: session.get('__gatewayUrl'),
}
entry = entry.replace(
`<!-- injectable script -->`,
`<script>${`window.injectData = ${JSON.stringify({
@@ -64,10 +102,18 @@ export class PageProxyController {
WEB_URL: webUrl,
INIT: await this.initService.isInit(),
} as IInjectableData)}`}
window.injectData.BASE_API = location.origin + '${
!isDev ? '/api/v' + API_VERSION : ''
}';
window.injectData.GATEWAY = location.origin;
${
sessionInjectableData.BASE_API
? `window.injectData.BASE_API = '${sessionInjectableData.BASE_API}'`
: `window.injectData.BASE_API = location.origin + '${
!isDev ? '/api/v' + API_VERSION : ''
}';`
}
${
sessionInjectableData.GATEWAY
? `window.injectData.GATEWAY = '${sessionInjectableData.GATEWAY}';`
: `window.injectData.GATEWAY = location.origin;`
}
</script>`,
)
return entry

View File

@@ -0,0 +1,24 @@
import { Transform } from 'class-transformer'
import { IsBoolean, IsIn, IsOptional, IsUrl } from 'class-validator'
export class PageProxyDebugDto {
@IsIn([false])
@IsOptional()
@Transform(({ value }) => (value === 'false' ? false : true))
__debug: boolean
@IsUrl({ require_protocol: true })
@IsOptional()
__apiUrl?: string
@IsUrl({ require_protocol: true })
@IsOptional()
__gatewayUrl?: string
@IsBoolean()
@IsOptional()
@Transform(({ value }) => (value === 'true' ? true : false))
/**
* If true, always use index.html pull from github.
*/
__onlyGithub = false
}