Skip to content

Commit

Permalink
feat(core): built-in console application (#604)
Browse files Browse the repository at this point in the history
  • Loading branch information
wibus-wee authored Jan 22, 2023
1 parent eec260a commit 7475fce
Show file tree
Hide file tree
Showing 12 changed files with 318 additions and 19 deletions.
2 changes: 2 additions & 0 deletions apps/core/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { ClientsModule } from '@nestjs/microservices';
import { ServicesEnum } from '~/shared/constants/services.constant';
import { ConfigPublicModule } from './modules/configs/configs.module';
import { REDIS_TRANSPORTER } from '~/shared/constants/transporter.constants';
import { ConsoleModule } from './modules/console/console.module';

@Module({
imports: [
Expand All @@ -37,6 +38,7 @@ import { REDIS_TRANSPORTER } from '~/shared/constants/transporter.constants';
CommentsModule,
FriendsModule,
ConfigPublicModule,
ConsoleModule,
ClientsModule.register([
{
name: ServicesEnum.notification,
Expand Down
2 changes: 1 addition & 1 deletion apps/core/src/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export async function bootstrap() {
SwaggerModule.setup('api-docs', app, document);
}

const listening_ip = getEnv(ServicesEnum.core)['listening_ip'] || '0.0.0.0';
const listening_ip = getEnv(ServicesEnum.core)?.['listening_ip'] || '0.0.0.0';

await app.listen(+PORT, listening_ip, async (err) => {
if (err) {
Expand Down
38 changes: 38 additions & 0 deletions apps/core/src/modules/console/console.controller.ts
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');
}
}
10 changes: 10 additions & 0 deletions apps/core/src/modules/console/console.interface.ts
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;
}
12 changes: 12 additions & 0 deletions apps/core/src/modules/console/console.module.ts
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 {}
192 changes: 192 additions & 0 deletions apps/core/src/modules/console/console.service.ts
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',
};
}
}
24 changes: 7 additions & 17 deletions env.yaml
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"consola": "2.15.3",
"cron": "2.2.0",
"dayjs": "1.11.7",
"fastify": "^4.12.0",
"js-yaml": "^4.1.0",
"jsdom": "^21.0.0",
"jsonwebtoken": "9.0.0",
Expand Down
28 changes: 28 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion shared/common/decorator/cookie.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export const Cookies = createParamDecorator(
const request = ctx.switchToHttp().getRequest();
// 获取 header 中的 cookie
const cookies = request.headers.cookie;
console.log(cookies);
// 解析 cookie
const cookie = cookies ? cookies.split('; ') : [];
const cookieObj = {};
Expand Down
6 changes: 6 additions & 0 deletions shared/constants/echo.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,10 @@ export enum ExceptionMessage {
FriendLinkIsNotExist = '友链不存在 o(╯□╰)o',
FriendLinkTokenIsInvalid = '友链Token无效,可能找错了哦',
FriendLinkIsExist = '友链已存在 o(╯□╰)o',

ConsoleFileIsNotExist = '@mogland/console 所期望请求的文件不存在,请提交 issues 至 mogland/console',
CONSOLE_INIT_FAILED = '@mogland/console 文件初始化失败,请提交 issues 至 mogland/console',
ConsoleInitSuccess = '控制台服务初始化成功',
ConsoleIsDisabled = '控制台服务已被禁用',
CONSOLE_REQUEST_FAILED = '控制台资源请求失败!',
}
Loading

0 comments on commit 7475fce

Please sign in to comment.