-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(core): built-in console application (#604)
- Loading branch information
Showing
12 changed files
with
318 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { Controller, Get, Inject, Req, Res } from '@nestjs/common'; | ||
import { FastifyRequest, FastifyReply } from 'fastify'; | ||
import { ConsoleService } from './console.service'; | ||
|
||
@Controller('console') | ||
export class ConsoleController { | ||
constructor( | ||
@Inject(ConsoleService) | ||
private readonly consoleService: ConsoleService, | ||
) {} | ||
|
||
@Get(['/*', '/']) | ||
async console(@Res() res: FastifyReply, @Req() req: FastifyRequest) { | ||
const path = req.url | ||
.replace('/console', '') | ||
.split('/') | ||
.pop() | ||
?.split('?')[0] | ||
.replace(/\/$/, ''); | ||
const file = await this.consoleService.transformPathToFile( | ||
path, | ||
req.headers.origin as string, | ||
); | ||
const contentType = | ||
file?.type === 'js' | ||
? 'application/javascript' | ||
: file?.type === 'css' | ||
? 'text/css' | ||
: file?.type === 'html' | ||
? 'text/html' | ||
: 'text/plain'; | ||
if (file) { | ||
res.header('Content-Type', contentType); | ||
res.send(file.data); | ||
} | ||
res.send('console'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export interface getPackageIntoInterface { | ||
version: string; | ||
packages: getPackageIntoFiles[]; | ||
} | ||
|
||
export interface getPackageIntoFiles { | ||
name: string; | ||
url: string; | ||
type: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { HelperModule } from '~/libs/helper/src'; | ||
import { ConsoleController } from './console.controller'; | ||
import { ConsoleService } from './console.service'; | ||
|
||
@Module({ | ||
imports: [HelperModule], | ||
controllers: [ConsoleController], | ||
providers: [ConsoleService], | ||
exports: [], | ||
}) | ||
export class ConsoleModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
import { JSDOM } from 'jsdom'; | ||
import { Injectable, Logger } from '@nestjs/common'; | ||
import { HttpService } from '~/libs/helper/src/helper.http.service'; | ||
import { ExceptionMessage } from '~/shared/constants/echo.constant'; | ||
import { consola } from '~/shared/global/consola.global'; | ||
import { NPMFiles } from '~/shared/types/npm'; | ||
import { | ||
getPackageIntoFiles, | ||
getPackageIntoInterface, | ||
} from './console.interface'; | ||
|
||
@Injectable() | ||
export class ConsoleService { | ||
private logger: Logger; | ||
private readonly env: { | ||
[key: string]: any; | ||
}; | ||
private files: getPackageIntoFiles[] = []; | ||
constructor(private readonly http: HttpService) { | ||
this.logger = new Logger(ConsoleService.name); | ||
this.env = JSON.parse(process.env.MOG_PRIVATE_ENV || '{}')?.console as { | ||
[key: string]: any; | ||
}; | ||
try { | ||
if (this.env?.enable) { | ||
if (this.env?.source !== 'npm') { | ||
this.getLatestVersionInfoFromGitHub().then((res) => { | ||
this.files = res.packages; | ||
consola.success( | ||
`[ConsoleService] ${ExceptionMessage.ConsoleInitSuccess}`, | ||
); | ||
}); | ||
} else { | ||
this.getLatestVersionInfoFromNpm().then((res) => { | ||
this.files = res.packages; | ||
if (res.version === 'NaN') { | ||
return; | ||
} | ||
consola.success( | ||
`[ConsoleService] ${ExceptionMessage.ConsoleInitSuccess}`, | ||
); | ||
}); | ||
} | ||
} else { | ||
consola.info(`[ConsoleService] ${ExceptionMessage.ConsoleIsDisabled}`); | ||
} | ||
} catch { | ||
this.logger.error(ExceptionMessage.CONSOLE_INIT_FAILED); | ||
} | ||
} | ||
|
||
/** | ||
* 从 GitHub Release 获取最新版本信息 | ||
*/ | ||
async getLatestVersionInfoFromGitHub(): Promise<getPackageIntoInterface> { | ||
const type = this.env?.versionType; | ||
const url = `https://api.github.com/repos/mogland/console/releases${ | ||
type === 'pre-release' ? '' : '/latest' | ||
}`; | ||
const res = await this.http | ||
.getAndCacheRequest(url) | ||
.then((res) => JSON.parse(res)) | ||
.catch(() => { | ||
this.logger.error(ExceptionMessage.CONSOLE_INIT_FAILED); | ||
return {}; | ||
}); | ||
if (!Object.keys(res).length) { | ||
return { | ||
version: 'NaN', | ||
packages: [], | ||
}; | ||
} | ||
const json = type !== 'pre-release' ? res : res[0]; | ||
const proxy = this.env?.proxy?.gh || 'https://ghproxy.com'; | ||
consola.info(`[ConsoleService] Mog Console Version: ${json.tag_name}`); | ||
return { | ||
version: json.tag_name, | ||
packages: json.assets.map((asset: any) => { | ||
const url = asset.browser_download_url; | ||
const name = url.split('/').pop(); | ||
return { | ||
name, | ||
url: `${proxy}/${url}`, | ||
type: name.split('.')?.pop() || 'unknown', | ||
}; | ||
}), | ||
}; | ||
} | ||
|
||
/** | ||
* 从 NPM 获取最新版本的信息 | ||
*/ | ||
async getLatestVersionInfoFromNpm(): Promise<getPackageIntoInterface> { | ||
const versionInfo = await this.http.axiosRef | ||
.get('https://registry.npmjs.org/@mogland/console') | ||
.then((res) => res.data?.['dist-tags']) | ||
.catch(() => { | ||
this.logger.error(ExceptionMessage.CONSOLE_INIT_FAILED); | ||
return {}; | ||
}); | ||
let version = | ||
this.env?.versionType === 'pre-release' | ||
? versionInfo?.['next'] | ||
: versionInfo?.['latest']; | ||
if (!version && this.env?.versionType === 'pre-release') { | ||
version = versionInfo?.['latest']; // 如果没有 next 版本,则使用 latest 版本 | ||
} | ||
consola.info(`[ConsoleService] Mog Console Version: ${version}`); | ||
let files: NPMFiles; | ||
try { | ||
files = await this.http | ||
.getAndCacheRequest( | ||
`https://www.npmjs.com/package/@mogland/console/v/${version}/index`, | ||
) | ||
.then((res) => JSON.parse(res)) | ||
.catch((err) => { | ||
console.log(err); | ||
this.logger.error(ExceptionMessage.CONSOLE_INIT_FAILED); | ||
return {}; | ||
}); | ||
} catch { | ||
// 此处的 JSON.parse 可能会抛出异常(如直接访问一个不存在的版本),因此需要 try catch | ||
this.logger.error(ExceptionMessage.CONSOLE_INIT_FAILED); | ||
this.logger.error( | ||
'无法获取到 NPM 的文件列表,因此 Mog 无法初始化 Console。', | ||
); | ||
return { | ||
version: 'NaN', | ||
packages: [], | ||
}; | ||
} | ||
const returns: getPackageIntoInterface = { | ||
version, | ||
packages: [], | ||
}; | ||
Object.entries(files.files).forEach(([key, value]) => { | ||
const name = key.split('/').pop(); | ||
returns.packages.push({ | ||
name: name!, | ||
url: `https://www.npmjs.com/package/@mogland/console/file/${value.hex}`, | ||
type: name!.split('.')?.pop() || 'unknown', | ||
}); | ||
}); | ||
return returns; | ||
} | ||
|
||
/** | ||
* 把路径转换为文件 | ||
* @param path 路径 | ||
*/ | ||
async transformPathToFile( | ||
path?: string, | ||
requestURL?: string, | ||
): Promise<{ | ||
data: string; | ||
type: string; | ||
}> { | ||
if (!path) { | ||
path = 'index.html'; | ||
} | ||
const index = this.files.find((file) => file.name === 'index.html'); | ||
const file = this.files.find((file) => file.name === path); | ||
return { | ||
data: await (file?.type === 'html' || | ||
file?.type === 'js' || | ||
file?.type === 'css' | ||
? this.http.getAndCacheRequest(file?.url || index!.url) | ||
: this.http.axiosRef.get(file?.url || index!.url) | ||
) | ||
.then((res) => { | ||
const data = | ||
file?.type === 'html' || file?.type === 'js' || file?.type === 'css' | ||
? res | ||
: res.data; | ||
if (file?.type ? file.type === 'html' : true) { | ||
const dom = new JSDOM(data); | ||
const document = dom.window.document; | ||
document.head.innerHTML += ` | ||
<!-- Injected by Mog Core, DO NOT REMOVE --> | ||
<script>window.MOG_BASE = "/console";window.MOG_API = "http://${requestURL}";</script> | ||
`; | ||
return dom.serialize(); | ||
} | ||
return data; | ||
}) | ||
.catch(() => { | ||
this.logger.error(ExceptionMessage.CONSOLE_REQUEST_FAILED); | ||
}), | ||
type: file?.type || 'html', | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,8 @@ | ||
core: | ||
port: 8080 | ||
allow_origins: [ | ||
'localhost:9528', | ||
'localhost:2323', | ||
'localhost:2222', | ||
] | ||
listening_ip: '0.0.0.0' | ||
user_service: | ||
host: 127.0.0.1 | ||
port: 2331 | ||
page_service: | ||
host: 127.0.0.1 | ||
port: 2332 | ||
|
||
collection_name: 'mog' | ||
# db_host: 'localhost' | ||
jwt_expire: 7d | ||
jwt_expire: 7d | ||
console: | ||
enable: true | ||
versionType: "pre-release" | ||
source: "gh" # gh or npm, default: gh | ||
proxy: | ||
gh: "https://getgit.my-api.cn" # default: https://ghproxy.com |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.