Skip to content

Commit

Permalink
feat(theme)!: download theme from github or custom url
Browse files Browse the repository at this point in the history
  • Loading branch information
wibus-wee committed Aug 15, 2022
1 parent 730945e commit 2622d66
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 7 deletions.
2 changes: 2 additions & 0 deletions src/global/index.global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { DATA_DIR, LOG_DIR, PLUGIN_DIR, PUBLIC_DIR, THEME_DIR } from "~/constant
import { consola, registerStdLogger } from "./consola.global";
import "./dayjs.global";
import { isDev } from "./env.global";
import { join } from "path";

// 建立目录
function mkdirs() {
Expand All @@ -20,6 +21,7 @@ function mkdirs() {
Logger.log(chalk.blue(`插件文件夹 已准备好: ${PLUGIN_DIR}`));

mkdirSync(THEME_DIR, { recursive: true });
mkdirSync(join(THEME_DIR, 'tmp'), { recursive: true });
Logger.log(chalk.blue(`主题文件夹 已准备好: ${THEME_DIR}`));

mkdirSync(PUBLIC_DIR, { recursive: true });
Expand Down
4 changes: 2 additions & 2 deletions src/modules/backup/backup.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ export class BackupService {
async restore(buffer: Buffer) {
// 检查buffer是否为tar.gz文件
if (!buffer.slice(0, 2).toString("hex").includes("1f8b")) {
throw new Error("不是tar.gz文件");
throw new BadRequestException("不是tar.gz文件");
}
// 检查大小
if (!buffer.length) {
throw new Error("文件为空");
throw new BadRequestException("文件为空");
}
this.logger.log("正在还原数据库...");
const dir = getMediumDateTime(new Date());
Expand Down
46 changes: 44 additions & 2 deletions src/modules/theme/theme.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { Controller, Get, NotFoundException, Param, Query, Res } from '@nestjs/common';
import { BadRequestException, Controller, Get, NotFoundException, Param, Query, Req, Res } from '@nestjs/common';
import { join } from 'path';
import { FastifyRequest } from 'fastify';
import { Auth } from '~/common/decorator/auth.decorator';
import { ApiName } from '~/common/decorator/openapi.decorator';
import { THEME_DIR } from '~/constants/path.constant';
Expand All @@ -22,6 +23,7 @@ import { CommentStatus, CommentType } from '../comments/comments.model';
import { PagerDto } from '~/shared/dto/pager.dto';
import { ApiOperation } from '@nestjs/swagger';
import { AggregateService } from '../aggregate/aggregate.service';
import { MultipartFile } from '@fastify/multipart';

@Controller('theme')
@ApiName
Expand All @@ -38,6 +40,7 @@ export class ThemeController {
private readonly aggregateService: AggregateService,
) { }

// 基本配置
private async basicProps() {
return {
site: {
Expand Down Expand Up @@ -89,6 +92,45 @@ export class ThemeController {
return this.themeService.turnOffTheme(name);
}

// ********************************************************
// 以下是关于管理主题下载 上传的接口

private async ValidMultipartFields(
req: FastifyRequest
): Promise<MultipartFile> {
const data = await req.file();

if (!data) {
throw new BadRequestException("仅供上传文件!");
}
if (data.fieldname != "file") {
throw new BadRequestException("字段必须为 file");
}

return data;
}

@Get('/admin/upload')
@ApiOperation({ summary: '上传主题' })
@Auth()
async uploadTheme(@Req() req: FastifyRequest, @Query("name") name: string) {
const data = await this.ValidMultipartFields(req);
const { mimetype } = data;
// 如果不是 tar.gz 文件,则抛出异常
if (mimetype != "application/x-gzip") {
throw new BadRequestException("仅支持 tar.gz 文件");
}
return await this.themeService.uploadThemeFile(await data.toBuffer(), name);
}

@Get('/admin/download')
@ApiOperation({ summary: '自定义链接或从 GitHub 下载主题并上传' })
@Auth()
async downloadThemeAndUpload(@Query("repo") repo: string, @Query("type") type: "GitHub" | "Custom") {
return await this.themeService.downloadRepoArchive(repo, type);
}


// ********************************************************
// 以下是针对静态资源访问的接口
@Get('/public/*')
Expand Down Expand Up @@ -122,7 +164,7 @@ export class ThemeController {
}

@Get(["/posts", "/posts/*"]) // 文章列表
@ApiOperation({ summary: '文章列表 (聚合携带分页器)'})
@ApiOperation({ summary: '文章列表 (聚合携带分页器)' })
async renderPosts(@Res() res, @Param("*") pageProp: any) {
const page = pageProp && pageProp.split("/")[1] || 1;
return await res.view(
Expand Down
49 changes: 46 additions & 3 deletions src/modules/theme/theme.service.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
import { BadRequestException, Injectable, Logger } from '@nestjs/common';
import { YAML } from 'zx-cjs';
import { join } from 'path';
import { quiet, YAML } from 'zx-cjs';
import mkdirp from 'mkdirp';
import { THEME_DIR } from '~/constants/path.constant';
import { HttpService } from '~/processors/helper/helper.http.service';
import { ThemeDto } from '../configs/configs.dto';
import { ConfigsService } from '../configs/configs.service';
import { writeFile } from 'fs/promises';
import { cp } from 'fs';

@Injectable()
export class ThemeService {
private logger: Logger;
private CORE_VERSION = require('../../../package.json').version
constructor(
private readonly configsService: ConfigsService,

private readonly http: HttpService,

) {
this.logger = new Logger(ThemeService.name)
}


private async turnOnThemeLibs(name: string) {
// 查找配置文件是否存在
const themeConfigFile = await fs
Expand All @@ -36,7 +43,7 @@ export class ThemeService {
) {
// 提醒建议使用的版本
this.CORE_VERSION !== themeConfig.recommend_version && this.logger.warn(`主题 ${name} 建议使用版本 ${themeConfig.recommend_version}`)
} else if ( themeConfig.support_max_version == 'latest' ) {} else {
} else if (themeConfig.support_max_version == 'latest') { } else {
throw new BadRequestException(`主题 ${name} 不支持当前版本`);
}

Expand Down Expand Up @@ -87,4 +94,40 @@ export class ThemeService {
return theme;
}

async downloadRepoArchive(repo: string, type: "GitHub" | "Custom") {
const url = type === "GitHub" ? `https://api.github.com/repos/${repo}/zipball` : repo;
const { data } = await this.http.axiosRef.get(url);
return await this.uploadThemeFile(data, repo.split('/')[1]);
}

async uploadThemeFile(buffer: Buffer, name: string) {
if (!buffer.slice(0, 2).toString("hex").includes("1f8b")) {
throw new BadRequestException("不是tar.gz文件");
}
// 检查大小
if (!buffer.length) {
throw new BadRequestException("文件为空");
}
this.logger.warn("正在上传主题文件...");
const themePath = join(THEME_DIR, name)
mkdirp.sync(themePath)
const tmpPath = join(THEME_DIR, "tmp", `${name}_tmp.tar.gz`)
try {
await writeFile(tmpPath, buffer) // 写入临时文件
cd(tmpPath)
await nothrow(quiet($`tar -xzf ${name}_tmp.tar.gz`)) // 解压
const files = await nothrow(quiet($`ls | grep -v ${name}_tmp.tar.gz`)) // 获取文件列表, 去除临时文件, 获取主题文件目录名
cd(join(THEME_DIR, "tmp", files[0])) // 进入主题目录
await nothrow(quiet($`mv * ${themePath}`)) // 将主题文件移动到主题目录
await nothrow(quiet($`rm -rf ${tmpPath}`)) // 删除临时文件
this.logger.warn("主题 ${name} 上传成功");
return `主题 ${name} 上传成功`;

} catch (e) {
this.logger.error(
`上传主题文件失败,请检查主题文件是否正确` || e.stderr
)
}
}

}

0 comments on commit 2622d66

Please sign in to comment.