diff --git a/src/app.module.ts b/src/app.module.ts index 5c9d1dd91..b5747ed00 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -25,6 +25,7 @@ import { RolesGuard } from "./common/guard/roles.guard"; import { AuthModule } from "./modules/auth/auth.module"; import { BackupModule } from "./modules/backup/backup.module"; import { MarkdownModule } from "./modules/markdown/markdown.module"; +import { ThemeModule } from './modules/theme/theme.module'; @Module({ imports: [ @@ -47,6 +48,7 @@ import { MarkdownModule } from "./modules/markdown/markdown.module"; InitModule, BackupModule, MarkdownModule, + ThemeModule, ], controllers: [AppController], providers: [ diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 022028a7e..73ae620ff 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -27,22 +27,24 @@ export async function bootstrap() { const configService = app.get(ConfigsService); const theme = configService.model.findOne({ name: "theme" }); const themeEnabled = theme?.value?.find((item) => item.enabled); - app.useStaticAssets({ - root: join(THEME_DIR, themeEnabled?.name || "default", "public"), - prefix: "/public/", - }); - app.setViewEngine({ - engine: { - handlebars: require("handlebars"), - "art-template": require("art-template"), - ejs: require("ejs"), - }, - templates: join(THEME_DIR, themeEnabled.name || "default"), - viewExt: themeEnabled.viewExt || "art-template", - defaultContext: { - dev: process.env.NODE_ENV === "development", - }, - }); + if (themeEnabled) { + app.useStaticAssets({ + root: join(THEME_DIR, themeEnabled?.name || "default", "public"), + prefix: "/public/", + }); + app.setViewEngine({ + engine: { + handlebars: require("handlebars"), + "art-template": require("art-template"), + ejs: require("ejs"), + }, + templates: join(THEME_DIR, themeEnabled.name || "default"), + viewExt: themeEnabled.viewExt || "art-template", + defaultContext: { + dev: process.env.NODE_ENV === "development", + }, + }); + } const hosts = Origin.map((host) => new RegExp(host, "i")); diff --git a/src/modules/configs/configs.service.ts b/src/modules/configs/configs.service.ts index 9f1208b67..c20c91dd7 100644 --- a/src/modules/configs/configs.service.ts +++ b/src/modules/configs/configs.service.ts @@ -21,9 +21,6 @@ import { BeAnObject } from "@typegoose/typegoose/lib/types"; import * as configDto from "./configs.dto"; import { validateSync, ValidatorOptions } from "class-validator"; import { ClassConstructor, plainToInstance } from "class-transformer"; -import { THEME_DIR } from "~/constants/path.constant"; -import { YAML } from "zx-cjs"; -import { ThemeDto } from "./configs.dto"; const allOptionKeys: Set = new Set(); const map: Record = Object.entries(configDto).reduce( @@ -226,57 +223,6 @@ export class ConfigsService { return config; } - CORE_VERSION = require('../../../package.json').version - - private async turnOnThemeLibs(name: string) { - // 查找配置文件是否存在 - const themeConfigFile = await fs - .readFile(`${THEME_DIR}/${name}/theme.yaml`, "utf8") - .catch(() => { - throw new BadRequestException(`主题 ${name} 配置文件不存在`); - }) - - const themeConfig = YAML.parse(themeConfigFile); // 解析配置文件 - - if (themeConfig.name !== name) { // 严格按照主题名称来配置,考虑大小写 - throw new BadRequestException(`主题 ${name} 配置文件名称不匹配`); - } - - // 检查主题是否适合当前后端的版本 - if ( - this.CORE_VERSION > themeConfig.support_min_version && - this.CORE_VERSION < themeConfig.support_max_version - ) { - // 提醒建议使用的版本 - this.CORE_VERSION !== themeConfig.recommend_version && this.logger.warn(`主题 ${name} 建议使用版本 ${themeConfig.recommend_version}`) - } else { - throw new BadRequestException(`主题 ${name} 不支持当前版本`); - } - - return { - themeConfig, - } - } - - /** - * turnOnTheme 启动主题 - */ - async turnOnTheme(name: string) { - - if (fs.existsSync(path.join(THEME_DIR, name))) { // 检查主题是否存在 - - const { themeConfig } = await this.turnOnThemeLibs(name); // 获取主题配置 - const theme: ThemeDto = { // 创建主题对象 - name, - enable: true, - configs: themeConfig.configs ? themeConfig.configs : {}, - viewExt: themeConfig.viewExt || 'art-template', - } - await this.patch('theme', theme); // 更新主题配置 - return `主题 ${name} 启动成功`; - } - } - get getMaster() { return this.userService.getMaster.bind(this.userService) as () => Promise< LeanDocument> diff --git a/src/modules/theme/theme.controller.ts b/src/modules/theme/theme.controller.ts new file mode 100644 index 000000000..7370bded6 --- /dev/null +++ b/src/modules/theme/theme.controller.ts @@ -0,0 +1,4 @@ +import { Controller } from '@nestjs/common'; + +@Controller('theme') +export class ThemeController {} diff --git a/src/modules/theme/theme.module.ts b/src/modules/theme/theme.module.ts new file mode 100644 index 000000000..6bc730643 --- /dev/null +++ b/src/modules/theme/theme.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { ThemeController } from './theme.controller'; +import { ThemeService } from './theme.service'; + +@Module({ + controllers: [ThemeController], + providers: [ThemeService] +}) +export class ThemeModule {} diff --git a/src/modules/theme/theme.service.ts b/src/modules/theme/theme.service.ts new file mode 100644 index 000000000..7788d48cb --- /dev/null +++ b/src/modules/theme/theme.service.ts @@ -0,0 +1,67 @@ +import { BadRequestException, Injectable, Logger } from '@nestjs/common'; +import { YAML } from 'zx-cjs'; +import { THEME_DIR } from '~/constants/path.constant'; +import { ThemeDto } from '../configs/configs.dto'; +import { ConfigsService } from '../configs/configs.service'; + +@Injectable() +export class ThemeService { + private logger: Logger; + constructor( + private readonly configsService: ConfigsService, + + ) { + this.logger = new Logger(ThemeService.name) + } + + + CORE_VERSION = require('../../../package.json').version + + private async turnOnThemeLibs(name: string) { + // 查找配置文件是否存在 + const themeConfigFile = await fs + .readFile(`${THEME_DIR}/${name}/theme.yaml`, "utf8") + .catch(() => { + throw new BadRequestException(`主题 ${name} 配置文件不存在`); + }) + + const themeConfig = YAML.parse(themeConfigFile); // 解析配置文件 + + if (themeConfig.name !== name) { // 严格按照主题名称来配置,考虑大小写 + throw new BadRequestException(`主题 ${name} 配置文件名称不匹配`); + } + + // 检查主题是否适合当前后端的版本 + if ( + this.CORE_VERSION > themeConfig.support_min_version && + this.CORE_VERSION < themeConfig.support_max_version + ) { + // 提醒建议使用的版本 + this.CORE_VERSION !== themeConfig.recommend_version && this.logger.warn(`主题 ${name} 建议使用版本 ${themeConfig.recommend_version}`) + } else { + throw new BadRequestException(`主题 ${name} 不支持当前版本`); + } + + return { + themeConfig, + } + } + + /** + * turnOnTheme 启动主题 + */ + async turnOnTheme(name: string) { + if (fs.existsSync(path.join(THEME_DIR, name))) { // 检查主题是否存在 + + const { themeConfig } = await this.turnOnThemeLibs(name); // 获取主题配置 + const theme: ThemeDto = { // 创建主题对象 + name, + enable: true, + configs: themeConfig.configs ? themeConfig.configs : {}, + viewExt: themeConfig.viewExt || 'art-template', + } + await this.configsService.patch('theme', theme); // 更新主题配置 + return `主题 ${name} 启动成功`; + } + } +} diff --git a/test/src/modules/theme/theme.controller.spec.ts b/test/src/modules/theme/theme.controller.spec.ts new file mode 100644 index 000000000..408e5f219 --- /dev/null +++ b/test/src/modules/theme/theme.controller.spec.ts @@ -0,0 +1,19 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { beforeEach, expect, describe, it } from "vitest"; +import { ThemeController } from '~/modules/theme/theme.controller'; + +describe('ThemeController', () => { + let controller: ThemeController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [ThemeController], + }).compile(); + + controller = module.get(ThemeController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/test/src/modules/theme/theme.service.spec.ts b/test/src/modules/theme/theme.service.spec.ts new file mode 100644 index 000000000..c2695e14f --- /dev/null +++ b/test/src/modules/theme/theme.service.spec.ts @@ -0,0 +1,19 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { beforeEach, expect, describe, it } from "vitest"; +import { ThemeService } from '~/modules/theme/theme.service'; + +describe('ThemeService', () => { + let service: ThemeService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ThemeService], + }).compile(); + + service = module.get(ThemeService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +});