diff --git a/apps/admin/.eslintrc.js b/apps/admin/.eslintrc.js
index 870eb08..98fc3ef 100644
--- a/apps/admin/.eslintrc.js
+++ b/apps/admin/.eslintrc.js
@@ -1,4 +1,7 @@
module.exports = {
root: true,
extends: ['@vben'],
+ rules: {
+ 'no-undef': 'off',
+ },
};
diff --git a/apps/admin/package.json b/apps/admin/package.json
index 1b3d73a..f4a7bdd 100644
--- a/apps/admin/package.json
+++ b/apps/admin/package.json
@@ -17,35 +17,6 @@
"test:gzip": "npx http-server dist --cors --gzip -c-1",
"type:check": "vue-tsc --noEmit --skipLibCheck"
},
- "lint-staged": {
- "*.{js,jsx,ts,tsx}": [
- "prettier --write",
- "eslint --fix"
- ],
- "{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
- "prettier --write--parser json"
- ],
- "package.json": [
- "prettier --write"
- ],
- "*.vue": [
- "prettier --write",
- "eslint --fix",
- "stylelint --fix"
- ],
- "*.{scss,less,styl,html}": [
- "prettier --write",
- "stylelint --fix"
- ],
- "*.md": [
- "prettier --write"
- ]
- },
- "config": {
- "commitizen": {
- "path": "node_modules/cz-git"
- }
- },
"dependencies": {
"@ant-design/icons-vue": "^7.0.1",
"@iconify/iconify": "^3.1.1",
diff --git a/apps/admin/src/components/Upload/index.ts b/apps/admin/src/components/Upload/index.ts
index 325a396..968ad26 100644
--- a/apps/admin/src/components/Upload/index.ts
+++ b/apps/admin/src/components/Upload/index.ts
@@ -1,6 +1,8 @@
import { withInstall } from '/@/utils';
import basicUpload from './src/BasicUpload.vue';
import uploadImage from './src/components/ImageUpload.vue';
+import avatarUpload from './src/AvatarUpload.vue';
export const ImageUpload = withInstall(uploadImage);
export const BasicUpload = withInstall(basicUpload);
+export const AvatarUpload = withInstall(avatarUpload);
diff --git a/apps/admin/src/components/Upload/src/AvatarUpload.vue b/apps/admin/src/components/Upload/src/AvatarUpload.vue
new file mode 100644
index 0000000..0d14670
--- /dev/null
+++ b/apps/admin/src/components/Upload/src/AvatarUpload.vue
@@ -0,0 +1,119 @@
+
+
+
+
diff --git a/apps/admin/src/utils/http/axios/Axios.ts b/apps/admin/src/utils/http/axios/Axios.ts
index e75dfdd..b215be9 100644
--- a/apps/admin/src/utils/http/axios/Axios.ts
+++ b/apps/admin/src/utils/http/axios/Axios.ts
@@ -88,8 +88,9 @@ export class VAxios {
// Request interceptor configuration processing
this.axiosInstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
- // If cancel repeat request is turned on, then cancel repeat request is prohibited
- const requestOptions = (config as unknown as any).requestOptions ?? this.options.requestOptions;
+ // If cancel repeat request is turned on, then cancel repeat request is prohibited
+ const requestOptions =
+ (config as unknown as any).requestOptions ?? this.options.requestOptions;
const ignoreCancelToken = requestOptions?.ignoreCancelToken ?? true;
!ignoreCancelToken && axiosCanceler.addPending(config);
@@ -202,7 +203,7 @@ export class VAxios {
if (config.cancelToken) {
conf.cancelToken = config.cancelToken;
}
-
+
if (config.signal) {
conf.signal = config.signal;
}
diff --git a/apps/admin/src/views/system/dict/dict.data.ts b/apps/admin/src/views/system/dict/dict.data.ts
index de8b0bf..4edba72 100644
--- a/apps/admin/src/views/system/dict/dict.data.ts
+++ b/apps/admin/src/views/system/dict/dict.data.ts
@@ -27,7 +27,7 @@ export const columns: BasicColumn[] = [
},
{
title: '更新时间',
- width: 150,
+ width: 160,
sorter: true,
dataIndex: 'updatedAt',
format: (text: string) => {
diff --git a/apps/admin/src/views/system/user/user.data.ts b/apps/admin/src/views/system/user/user.data.ts
index c323c3b..c5a1017 100644
--- a/apps/admin/src/views/system/user/user.data.ts
+++ b/apps/admin/src/views/system/user/user.data.ts
@@ -67,10 +67,8 @@ export const columns: BasicColumn[] = [
{
title: '创建时间',
dataIndex: 'createdAt',
- width: 165,
- format: (text) => {
- return formatToDateTime(text);
- },
+ width: 160,
+ format: (text) => formatToDateTime(text),
},
];
diff --git a/apps/admin/src/views/tools/storage/storage.data.ts b/apps/admin/src/views/tools/storage/storage.data.ts
index 21ff13d..24769e8 100644
--- a/apps/admin/src/views/tools/storage/storage.data.ts
+++ b/apps/admin/src/views/tools/storage/storage.data.ts
@@ -48,10 +48,8 @@ export const columns: BasicColumn[] = [
{
title: '创建时间',
dataIndex: 'createdAt',
- width: 150,
- format: (text) => {
- return formatToDateTime(text);
- },
+ width: 160,
+ format: (text) => formatToDateTime(text),
},
];
diff --git a/apps/api/.env b/apps/api/.env
index 6720e65..d62b577 100644
--- a/apps/api/.env
+++ b/apps/api/.env
@@ -1,24 +1,10 @@
+# app
APP_NAME = Nest Admin
-
-# server port
-PORT = 5001
+APP_PORT = 5001
+APP_LOCALE = zh-CN
WS_PORT = 5002
WS_PATH = ws-api
-# global prefix, using in router、redis
-GLOBAL_PREFIX = api
-
-# specify id as the root administrator
-ADMIN_ROLE_ID = 1
-# user password salt
-USER_PWD_SALT = kz!@#123
-# user default password
-USER_DEFAULT_PWD = a123456
-
-# minimum internal requirements protect id
-PROTECT_SYS_PERMMENU_MAX_ID = 1
-PROTECT_SYS_DICTIONARY_MAX_ID = 1
-
# logger
LOGGER_LEVEL = verbose
LOGGER_MAX_FILES = 31
diff --git a/apps/api/.env.development b/apps/api/.env.development
index 5a58b3d..34df15c 100644
--- a/apps/api/.env.development
+++ b/apps/api/.env.development
@@ -1,8 +1,16 @@
-# token
+# logger
+LOGGER_LEVEL = debug
+
+# security
JWT_SECRET = admin!@#123
-JWT_EXPIRES = 86400
+JWT_EXPIRE = 86400
REFRESH_TOKEN_SECRET = admin!@#123
-REFRESH_TOKEN_EXPIRES = 2592000
+REFRESH_TOKEN_EXPIRE = 2592000
+
+# swagger
+SWAGGER_ENABLE = true
+SWAGGER_PATH = api-docs
+SWAGGER_VERSION = 1.0
# db
DB_HOST = 127.0.0.1
@@ -19,12 +27,8 @@ REDIS_HOST = 127.0.0.1
REDIS_PASSWORD =
REDIS_DB = 0
-# swagger
-SWAGGER_ENABLE = true
-SWAGGER_PATH = api-docs
-
-# email
-EMAIL_HOST = smtp.qq.com
-EMAIL_PORT = 465
-EMAIL_USER =
-EMAIL_PASS =
+# smtp
+SMTP_HOST = smtp.qq.com
+SMTP_PORT = 465
+SMTP_USER =
+SMTP_PASS =
diff --git a/apps/api/.env.production b/apps/api/.env.production
index 3e940dd..71fba48 100644
--- a/apps/api/.env.production
+++ b/apps/api/.env.production
@@ -1,16 +1,24 @@
-# token
+# logger
+LOGGER_LEVEL = info
+
+# security
JWT_SECRET = admin!@#123
-JWT_EXPIRES = 86400
+JWT_EXPIRE = 86400
REFRESH_TOKEN_SECRET = admin!@#123
-REFRESH_TOKEN_EXPIRES = 2592000
+REFRESH_TOKEN_EXPIRE = 2592000
+
+# swagger
+SWAGGER_ENABLE = true
+SWAGGER_PATH = api-docs
+SWAGGER_VERSION = 1.0
# db
DB_HOST = 127.0.0.1
DB_PORT = 3306
-DB_DATABASE = kz-admin
-DB_USERNAME = kz-admin
+DB_DATABASE = qb360
+DB_USERNAME = root
DB_PASSWORD = Aa123456
-DB_SYNCHRONIZE = false
+DB_SYNCHRONIZE = true
DB_LOGGING = ["error"]
# redis
@@ -19,14 +27,8 @@ REDIS_HOST = 127.0.0.1
REDIS_PASSWORD =
REDIS_DB = 0
-# swagger
-SWAGGER_ENABLE = false
-
-# email
-EMAIL_HOST = smtp.qq.com
-EMAIL_PORT = 465
-EMAIL_USER =
-EMAIL_PASS =
-
-# logger
-LOGGER_LEVEL = warn
+# smtp
+SMTP_HOST = smtp.qq.com
+SMTP_PORT = 465
+SMTP_USER =
+SMTP_PASS =
diff --git a/apps/api/package.json b/apps/api/package.json
index 62aadaa..2721b1b 100644
--- a/apps/api/package.json
+++ b/apps/api/package.json
@@ -31,6 +31,7 @@
"migration:revert": "typeorm -- migration:revert"
},
"dependencies": {
+ "@fastify/cookie": "^9.1.0",
"@fastify/multipart": "^8.0.0",
"@fastify/static": "^6.12.0",
"@liaoliaots/nestjs-redis": "^9.0.5",
@@ -68,7 +69,6 @@
"helmet": "^7.1.0",
"ioredis": "^5.3.2",
"lodash": "^4.17.21",
- "log4js": "^6.9.1",
"mysql": "^2.18.1",
"nanoid": "^3.3.6",
"nodemailer": "^6.9.7",
@@ -83,7 +83,9 @@
"svg-captcha": "^1.4.0",
"systeminformation": "^5.21.16",
"typeorm": "^0.3.17",
- "ua-parser-js": "^1.0.37"
+ "ua-parser-js": "^1.0.37",
+ "winston": "^3.11.0",
+ "winston-daily-rotate-file": "^4.7.1"
},
"devDependencies": {
"@compodoc/compodoc": "^1.1.22",
@@ -135,5 +137,9 @@
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
+ },
+ "engines": {
+ "node": ">=18",
+ "pnpm": ">=8.1.0"
}
}
diff --git a/apps/api/src/app.module.ts b/apps/api/src/app.module.ts
index 87d3062..4f8d1de 100644
--- a/apps/api/src/app.module.ts
+++ b/apps/api/src/app.module.ts
@@ -1,27 +1,34 @@
import { Module } from '@nestjs/common';
-import { APP_GUARD } from '@nestjs/core';
+import { ConfigModule } from '@nestjs/config';
+import { APP_FILTER, APP_GUARD } from '@nestjs/core';
-import { SharedModule } from '@/modules/shared/shared.module';
+import * as config from '@/config';
+import { SharedModule } from '@/shared/shared.module';
-import { AppConfigModule } from './config/config.module';
-import { AppDatabaseModule } from './database/database.module';
+import { AllExceptionsFilter } from './common/filters/any-exception.filter';
-import { AppsModule } from './modules/apps/apps.module';
+import { TodoModule } from './modules/todo/todo.module';
import { AuthModule } from './modules/auth/auth.module';
import { JwtAuthGuard } from './modules/auth/guards/jwt-auth.guard';
+import { RbacGuard } from './modules/auth/guards/rbac.guard';
import { HealthModule } from './modules/health/health.module';
-import { RbacGuard } from './modules/rbac/guards/rbac.guard';
import { SocketModule } from './modules/socket/socket.module';
import { SystemModule } from './modules/system/system.module';
import { TasksModule } from './modules/tasks/tasks.module';
import { ToolsModule } from './modules/tools/tools.module';
+import { DatabaseModule } from './shared/database/database.module';
@Module({
imports: [
- AppConfigModule,
- AppDatabaseModule,
+ ConfigModule.forRoot({
+ isGlobal: true,
+ envFilePath: [`.env.${process.env.NODE_ENV}`, '.env'],
+ load: [...Object.values(config)],
+ }),
SharedModule,
+ DatabaseModule,
+
AuthModule,
SystemModule,
TasksModule,
@@ -29,17 +36,17 @@ import { ToolsModule } from './modules/tools/tools.module';
SocketModule,
HealthModule,
- AppsModule,
+ // biz
+
+ // end biz
+
+ TodoModule,
],
providers: [
- {
- provide: APP_GUARD,
- useClass: JwtAuthGuard,
- },
- {
- provide: APP_GUARD,
- useClass: RbacGuard,
- },
+ { provide: APP_FILTER, useClass: AllExceptionsFilter },
+
+ { provide: APP_GUARD, useClass: JwtAuthGuard },
+ { provide: APP_GUARD, useClass: RbacGuard },
],
})
export class AppModule {}
diff --git a/apps/api/src/common/adapters/fastify.adapter.ts b/apps/api/src/common/adapters/fastify.adapter.ts
new file mode 100644
index 0000000..8e72f92
--- /dev/null
+++ b/apps/api/src/common/adapters/fastify.adapter.ts
@@ -0,0 +1,47 @@
+import FastifyCookie from '@fastify/cookie';
+import FastifyMultipart from '@fastify/multipart';
+import { FastifyAdapter } from '@nestjs/platform-fastify';
+
+const app: FastifyAdapter = new FastifyAdapter({
+ trustProxy: true,
+ logger: false,
+});
+export { app as fastifyApp };
+
+app.register(FastifyMultipart as any, {
+ limits: {
+ fields: 10, // Max number of non-file fields
+ fileSize: 1024 * 1024 * 6, // limit size 6M
+ files: 5, // Max number of file fields
+ },
+});
+
+app.register(FastifyCookie as any, {
+ secret: 'cookie-secret', // 这个 secret 不太重要,不存鉴权相关,无关紧要
+});
+
+app.getInstance().addHook('onRequest', (request, reply, done) => {
+ // set undefined origin
+ const { origin } = request.headers;
+ if (!origin) {
+ request.headers.origin = request.headers.host;
+ }
+
+ // forbidden php
+
+ const { url } = request;
+
+ if (url.endsWith('.php')) {
+ reply.raw.statusMessage =
+ 'Eh. PHP is not support on this machine. Yep, I also think PHP is bestest programming language. But for me it is beyond my reach.';
+
+ return reply.code(418).send();
+ }
+
+ // skip favicon request
+ if (url.match(/favicon.ico$/) || url.match(/manifest.json$/)) {
+ return reply.code(204).send();
+ }
+
+ done();
+});
diff --git a/apps/api/src/common/adapters/socket.adapter.ts b/apps/api/src/common/adapters/socket.adapter.ts
new file mode 100644
index 0000000..ba6bf60
--- /dev/null
+++ b/apps/api/src/common/adapters/socket.adapter.ts
@@ -0,0 +1,3 @@
+import { IoAdapter } from '@nestjs/platform-socket.io';
+
+export { IoAdapter };
diff --git a/apps/api/src/decorators/api-result.decorator.ts b/apps/api/src/common/decorators/api-result.decorator.ts
similarity index 100%
rename from apps/api/src/decorators/api-result.decorator.ts
rename to apps/api/src/common/decorators/api-result.decorator.ts
diff --git a/apps/api/src/decorators/api-token.decorator.ts b/apps/api/src/common/decorators/api-token.decorator.ts
similarity index 100%
rename from apps/api/src/decorators/api-token.decorator.ts
rename to apps/api/src/common/decorators/api-token.decorator.ts
diff --git a/apps/api/src/common/decorators/cookie.decorator.ts b/apps/api/src/common/decorators/cookie.decorator.ts
new file mode 100644
index 0000000..11be959
--- /dev/null
+++ b/apps/api/src/common/decorators/cookie.decorator.ts
@@ -0,0 +1,10 @@
+import type { ExecutionContext } from '@nestjs/common';
+import { createParamDecorator } from '@nestjs/common';
+import type { FastifyRequest } from 'fastify';
+
+export const Cookies = createParamDecorator(
+ (data: string, ctx: ExecutionContext) => {
+ const request = ctx.switchToHttp().getRequest();
+ return data ? request.cookies?.[data] : request.cookies;
+ },
+);
diff --git a/apps/api/src/decorators/field.decorator.ts b/apps/api/src/common/decorators/field.decorator.ts
similarity index 100%
rename from apps/api/src/decorators/field.decorator.ts
rename to apps/api/src/common/decorators/field.decorator.ts
diff --git a/apps/api/src/decorators/http.decorator.ts b/apps/api/src/common/decorators/http.decorator.ts
similarity index 100%
rename from apps/api/src/decorators/http.decorator.ts
rename to apps/api/src/common/decorators/http.decorator.ts
diff --git a/apps/api/src/decorators/id-param.decorator.ts b/apps/api/src/common/decorators/id-param.decorator.ts
similarity index 100%
rename from apps/api/src/decorators/id-param.decorator.ts
rename to apps/api/src/common/decorators/id-param.decorator.ts
diff --git a/apps/api/src/decorators/skip-transform.decorator.ts b/apps/api/src/common/decorators/skip-transform.decorator.ts
similarity index 100%
rename from apps/api/src/decorators/skip-transform.decorator.ts
rename to apps/api/src/common/decorators/skip-transform.decorator.ts
diff --git a/apps/api/src/decorators/swagger.decorator.ts b/apps/api/src/common/decorators/swagger.decorator.ts
similarity index 100%
rename from apps/api/src/decorators/swagger.decorator.ts
rename to apps/api/src/common/decorators/swagger.decorator.ts
diff --git a/apps/api/src/decorators/transform.decorator.ts b/apps/api/src/common/decorators/transform.decorator.ts
similarity index 100%
rename from apps/api/src/decorators/transform.decorator.ts
rename to apps/api/src/common/decorators/transform.decorator.ts
diff --git a/apps/api/src/common/dto/abstract.dto.ts b/apps/api/src/common/dto/abstract.dto.ts
index d35569f..8c7ff84 100644
--- a/apps/api/src/common/dto/abstract.dto.ts
+++ b/apps/api/src/common/dto/abstract.dto.ts
@@ -1,7 +1,7 @@
import { Type } from 'class-transformer';
import { IsDate, IsInt, IsOptional, Min } from 'class-validator';
-export abstract class AbstractDTO {
+export abstract class AbstractDto {
@Type(() => Number)
@IsInt()
@Min(1)
diff --git a/apps/api/src/common/dto/delete.dto.ts b/apps/api/src/common/dto/delete.dto.ts
new file mode 100644
index 0000000..6f87b9e
--- /dev/null
+++ b/apps/api/src/common/dto/delete.dto.ts
@@ -0,0 +1,8 @@
+import { IsDefined, IsNotEmpty, IsNumber } from 'class-validator';
+
+export class BatchDeleteDto {
+ @IsDefined()
+ @IsNotEmpty()
+ @IsNumber({}, { each: true })
+ ids: number[];
+}
diff --git a/apps/api/src/common/entity/abstract.entity.ts b/apps/api/src/common/entity/abstract.entity.ts
index a9ce4e6..b1b745f 100644
--- a/apps/api/src/common/entity/abstract.entity.ts
+++ b/apps/api/src/common/entity/abstract.entity.ts
@@ -8,9 +8,9 @@ export abstract class AbstractEntity {
@PrimaryGeneratedColumn()
id: number;
- @CreateDateColumn({ type: 'timestamp', name: 'created_at' })
+ @CreateDateColumn({ name: 'created_at' })
createdAt: Date;
- @UpdateDateColumn({ type: 'timestamp', name: 'updated_at' })
+ @UpdateDateColumn({ name: 'updated_at' })
updatedAt: Date;
}
diff --git a/apps/api/src/exceptions/api.exception.ts b/apps/api/src/common/exceptions/biz.exception.ts
similarity index 61%
rename from apps/api/src/exceptions/api.exception.ts
rename to apps/api/src/common/exceptions/biz.exception.ts
index 8c4e6aa..913375b 100644
--- a/apps/api/src/exceptions/api.exception.ts
+++ b/apps/api/src/common/exceptions/biz.exception.ts
@@ -1,23 +1,22 @@
import { HttpException, HttpStatus } from '@nestjs/common';
-import { ErrorEnum } from '../constants/error-code.constant';
+import { ErrorEnum } from '@/constants/error-code.constant';
+import { RESPONSE_SUCCESS_CODE } from '@/constants/response.constant';
-/**
- * 业务错误时可抛出该异常
- */
-export class ApiException extends HttpException {
+export class BusinessException extends HttpException {
private errorCode: number;
constructor(error: ErrorEnum | string) {
+ // 如果是非 ErrorEnum
if (!error.includes(':')) {
super(
HttpException.createBody({
- code: 0,
+ code: RESPONSE_SUCCESS_CODE,
message: error,
}),
HttpStatus.OK,
);
- this.errorCode = 0;
+ this.errorCode = RESPONSE_SUCCESS_CODE;
return;
}
@@ -37,3 +36,5 @@ export class ApiException extends HttpException {
return this.errorCode;
}
}
+
+export { BusinessException as BizException };
diff --git a/apps/api/src/common/exceptions/not-found.exception.ts b/apps/api/src/common/exceptions/not-found.exception.ts
new file mode 100644
index 0000000..95b034d
--- /dev/null
+++ b/apps/api/src/common/exceptions/not-found.exception.ts
@@ -0,0 +1,10 @@
+import { NotFoundException } from '@nestjs/common';
+import { sample } from 'lodash';
+
+export const NotFoundMessage = ['404, Not Found'];
+
+export class CannotFindException extends NotFoundException {
+ constructor() {
+ super(sample(NotFoundMessage));
+ }
+}
diff --git a/apps/api/src/exceptions/socket.exception.ts b/apps/api/src/common/exceptions/socket.exception.ts
similarity index 92%
rename from apps/api/src/exceptions/socket.exception.ts
rename to apps/api/src/common/exceptions/socket.exception.ts
index 3b55fe1..8d6219f 100644
--- a/apps/api/src/exceptions/socket.exception.ts
+++ b/apps/api/src/common/exceptions/socket.exception.ts
@@ -1,7 +1,7 @@
import { HttpException } from '@nestjs/common';
import { WsException } from '@nestjs/websockets';
-import { ErrorEnum } from '../constants/error-code.constant';
+import { ErrorEnum } from '@/constants/error-code.constant';
export class SocketException extends WsException {
private errorCode: number;
diff --git a/apps/api/src/common/filters/any-exception.filter.ts b/apps/api/src/common/filters/any-exception.filter.ts
new file mode 100644
index 0000000..5409a7e
--- /dev/null
+++ b/apps/api/src/common/filters/any-exception.filter.ts
@@ -0,0 +1,91 @@
+import {
+ ArgumentsHost,
+ Catch,
+ ExceptionFilter,
+ HttpException,
+ HttpStatus,
+ Logger,
+} from '@nestjs/common';
+import { FastifyReply, FastifyRequest } from 'fastify';
+
+import { BusinessException } from '@/common/exceptions/biz.exception';
+import { ErrorEnum } from '@/constants/error-code.constant';
+
+import { isDev } from '@/global/env';
+
+type myError = {
+ readonly status: number;
+ readonly statusCode?: number;
+
+ readonly message?: string;
+};
+
+@Catch()
+export class AllExceptionsFilter implements ExceptionFilter {
+ private readonly logger = new Logger(AllExceptionsFilter.name);
+
+ constructor() {
+ this.registerCatchAllExceptionsHook();
+ }
+
+ catch(exception: unknown, host: ArgumentsHost) {
+ const ctx = host.switchToHttp();
+ const request = ctx.getRequest();
+ const response = ctx.getResponse();
+
+ const url = request.raw.url!;
+
+ const status =
+ exception instanceof HttpException
+ ? exception.getStatus()
+ : (exception as myError)?.status ||
+ (exception as myError)?.statusCode ||
+ HttpStatus.INTERNAL_SERVER_ERROR;
+
+ let message =
+ (exception as any)?.response?.message ||
+ (exception as myError)?.message ||
+ `${exception}`;
+
+ // 系统内部错误时
+ if (
+ status === HttpStatus.INTERNAL_SERVER_ERROR &&
+ !(exception instanceof BusinessException)
+ ) {
+ Logger.error(exception, undefined, 'Catch');
+
+ // 生产环境下隐藏错误信息
+ if (!isDev) {
+ message = ErrorEnum.SERVER_ERROR?.split(':')[1];
+ }
+ } else {
+ this.logger.warn(
+ `错误信息:(${status}) ${message} Path: ${decodeURI(url)}`,
+ );
+ }
+
+ const apiErrorCode: number =
+ exception instanceof BusinessException
+ ? exception.getErrorCode()
+ : status;
+
+ // 返回基础响应结果
+ const resBody: IBaseResponse = {
+ code: apiErrorCode,
+ message,
+ data: null,
+ };
+
+ response.status(status).send(resBody);
+ }
+
+ registerCatchAllExceptionsHook() {
+ process.on('unhandledRejection', (reason) => {
+ console.error('unhandledRejection: ', reason);
+ });
+
+ process.on('uncaughtException', (err) => {
+ console.error('uncaughtException: ', err);
+ });
+ }
+}
diff --git a/apps/api/src/interceptors/logging.interceptor.ts b/apps/api/src/common/interceptors/logging.interceptor.ts
similarity index 72%
rename from apps/api/src/interceptors/logging.interceptor.ts
rename to apps/api/src/common/interceptors/logging.interceptor.ts
index 4efa6f1..8f5d1f7 100644
--- a/apps/api/src/interceptors/logging.interceptor.ts
+++ b/apps/api/src/common/interceptors/logging.interceptor.ts
@@ -9,11 +9,7 @@ import { Observable, tap } from 'rxjs';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
- private logger: Logger;
-
- constructor() {
- this.logger = new Logger(LoggingInterceptor.name, { timestamp: false });
- }
+ private logger = new Logger(LoggingInterceptor.name, { timestamp: false });
intercept(
context: ExecutionContext,
@@ -27,9 +23,7 @@ export class LoggingInterceptor implements NestInterceptor {
return call$.pipe(
tap(() =>
- this.logger.debug(
- `--- 响应请求:${content}${` +${Date.now() - now}ms`}`,
- ),
+ this.logger.debug(`--- 响应:${content}${` +${Date.now() - now}ms`}`),
),
);
}
diff --git a/apps/api/src/interceptors/timeout.interceptor.ts b/apps/api/src/common/interceptors/timeout.interceptor.ts
similarity index 92%
rename from apps/api/src/interceptors/timeout.interceptor.ts
rename to apps/api/src/common/interceptors/timeout.interceptor.ts
index 5d4fde1..5387869 100644
--- a/apps/api/src/interceptors/timeout.interceptor.ts
+++ b/apps/api/src/common/interceptors/timeout.interceptor.ts
@@ -10,7 +10,7 @@ import { catchError, timeout } from 'rxjs/operators';
@Injectable()
export class TimeoutInterceptor implements NestInterceptor {
- constructor(private readonly time: number = 5000) {}
+ constructor(private readonly time: number = 10000) {}
intercept(context: ExecutionContext, next: CallHandler): Observable {
return next.handle().pipe(
diff --git a/apps/api/src/interceptors/transform.interceptor.ts b/apps/api/src/common/interceptors/transform.interceptor.ts
similarity index 85%
rename from apps/api/src/interceptors/transform.interceptor.ts
rename to apps/api/src/common/interceptors/transform.interceptor.ts
index 67414e6..2492c5b 100644
--- a/apps/api/src/interceptors/transform.interceptor.ts
+++ b/apps/api/src/common/interceptors/transform.interceptor.ts
@@ -30,10 +30,10 @@ export class TransformInterceptor implements NestInterceptor {
return next.handle().pipe(
map((data) => {
- if (typeof data === 'undefined') {
- context.switchToHttp().getResponse().status(HttpStatus.NO_CONTENT);
- return data;
- }
+ // if (typeof data === 'undefined') {
+ // context.switchToHttp().getResponse().status(HttpStatus.NO_CONTENT);
+ // return data;
+ // }
return new ResOp(HttpStatus.OK, data ?? null);
}),
diff --git a/apps/api/src/common/model/response.model.ts b/apps/api/src/common/model/response.model.ts
index 791c500..b2eb2b4 100644
--- a/apps/api/src/common/model/response.model.ts
+++ b/apps/api/src/common/model/response.model.ts
@@ -1,23 +1,28 @@
import { ApiProperty } from '@nestjs/swagger';
+import {
+ RESPONSE_SUCCESS_CODE,
+ RESPONSE_SUCCESS_MSG,
+} from '@/constants/response.constant';
+
export class ResOp {
@ApiProperty({ type: 'object' })
data?: T;
- @ApiProperty({ type: 'number', default: 200 })
+ @ApiProperty({ type: 'number', default: RESPONSE_SUCCESS_CODE })
code: number;
- @ApiProperty({ type: 'string', default: 'success' })
+ @ApiProperty({ type: 'string', default: RESPONSE_SUCCESS_MSG })
message: string;
- constructor(code: number, data: T, message = 'success') {
+ constructor(code: number, data: T, message = RESPONSE_SUCCESS_MSG) {
this.code = code;
this.data = data;
this.message = message;
}
static success(data?: T, message?: string) {
- return new ResOp(200, data, message);
+ return new ResOp(RESPONSE_SUCCESS_CODE, data, message);
}
static error(code: number, message) {
diff --git a/apps/api/src/pipes/parse-int.pipe.ts b/apps/api/src/common/pipes/parse-int.pipe.ts
similarity index 100%
rename from apps/api/src/pipes/parse-int.pipe.ts
rename to apps/api/src/common/pipes/parse-int.pipe.ts
diff --git a/apps/api/src/config/app.config.ts b/apps/api/src/config/app.config.ts
new file mode 100644
index 0000000..d669411
--- /dev/null
+++ b/apps/api/src/config/app.config.ts
@@ -0,0 +1,20 @@
+import { ConfigType, registerAs } from '@nestjs/config';
+
+import { env, envNumber } from '@/global/env';
+
+export const AppConfig = registerAs('app', () => ({
+ name: env('APP_NAME'),
+ port: envNumber('APP_PORT', 3000),
+ globalPrefix: env('GLOBAL_PREFIX'),
+ adminRoleId: envNumber('ADMIN_ROLE_ID', 1),
+ userPwdSalt: env('USER_PWD_SALT', ''),
+ userDefaultPwd: env('USER_DEFAULT_PWD', 'a123456'),
+ locale: env('APP_LOCALE', 'zh-CN'),
+
+ logger: {
+ level: env('LOGGER_LEVEL'),
+ maxFiles: envNumber('LOGGER_MAX_FILES'),
+ },
+}));
+
+export type IAppConfig = ConfigType;
diff --git a/apps/api/src/config/config.module.ts b/apps/api/src/config/config.module.ts
deleted file mode 100644
index 3b1673c..0000000
--- a/apps/api/src/config/config.module.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Module } from '@nestjs/common';
-import { ConfigModule } from '@nestjs/config';
-
-import * as config from '@/config';
-
-@Module({
- imports: [
- ConfigModule.forRoot({
- isGlobal: true,
- envFilePath: [`.env.${process.env.NODE_ENV}`, '.env'],
- load: [...Object.values(config)],
- }),
- ],
-})
-export class AppConfigModule {}
diff --git a/apps/api/src/config/modules/database.config.ts b/apps/api/src/config/database.config.ts
similarity index 77%
rename from apps/api/src/config/modules/database.config.ts
rename to apps/api/src/config/database.config.ts
index 76202eb..9239b03 100644
--- a/apps/api/src/config/modules/database.config.ts
+++ b/apps/api/src/config/database.config.ts
@@ -1,8 +1,8 @@
-import { registerAs } from '@nestjs/config';
+import { ConfigType, registerAs } from '@nestjs/config';
import { DataSource, DataSourceOptions } from 'typeorm';
-import { env, envBoolean, envNumber } from '@/config/env';
+import { env, envBoolean, envNumber } from '@/global/env';
// eslint-disable-next-line import/order
import dotenv from 'dotenv';
@@ -22,12 +22,12 @@ const dataSourceOptions: DataSourceOptions = {
subscribers: ['dist/modules/**/*.subscriber{.ts,.js}'],
};
-export const database = registerAs(
+export const DatabaseConfig = registerAs(
'database',
(): DataSourceOptions => dataSourceOptions,
);
-export type IDatabaseConfig = ReturnType;
+export type IDatabaseConfig = ConfigType;
const dataSource = new DataSource(dataSourceOptions);
diff --git a/apps/api/src/config/env.ts b/apps/api/src/config/env.ts
deleted file mode 100644
index 246a49f..0000000
--- a/apps/api/src/config/env.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * 基础类型接口
- */
-export type BaseType = boolean | number | string | undefined | null;
-
-/**
- * 格式化环境变量
- * @param key 环境变量的键值
- * @param defaultValue 默认值
- * @param callback 格式化函数
- */
-const fromatValue = (
- key: string,
- defaultValue: T,
- callback?: (value: string) => T,
-): T => {
- const value: string | undefined = process.env[key];
- if (typeof value === 'undefined') {
- return defaultValue;
- }
- if (!callback) {
- return value as unknown as T;
- }
- return callback(value);
-};
-
-export const env = (key: string, defaultValue: string = '') =>
- fromatValue(key, defaultValue);
-
-export const envString = (key: string, defaultValue: string = '') =>
- fromatValue(key, defaultValue);
-
-export const envNumber = (key: string, defaultValue: number = 0) =>
- fromatValue(key, defaultValue, (value) => {
- try {
- return Number(value);
- } catch {
- throw new Error(`${key} environment variable is not a number`);
- }
- });
-
-export const envBoolean = (key: string, defaultValue: boolean = false) =>
- fromatValue(key, defaultValue, (value) => {
- try {
- return Boolean(JSON.parse(value));
- } catch {
- throw new Error(`${key} environment variable is not a boolean`);
- }
- });
diff --git a/apps/api/src/config/index.ts b/apps/api/src/config/index.ts
index 1f6e451..2090038 100644
--- a/apps/api/src/config/index.ts
+++ b/apps/api/src/config/index.ts
@@ -1,7 +1,7 @@
-export * from './modules/app.config';
-export * from './modules/redis.config';
-export * from './modules/database.config';
-export * from './modules/swagger.config';
-export * from './modules/jwt.config';
-export * from './modules/mailer.config';
-export * from './modules/sms.config';
+export * from './app.config';
+export * from './redis.config';
+export * from './database.config';
+export * from './swagger.config';
+export * from './security.config';
+export * from './mailer.config';
+export * from './sms.config';
diff --git a/apps/api/src/config/mailer.config.ts b/apps/api/src/config/mailer.config.ts
new file mode 100644
index 0000000..9255a60
--- /dev/null
+++ b/apps/api/src/config/mailer.config.ts
@@ -0,0 +1,16 @@
+import { ConfigType, registerAs } from '@nestjs/config';
+
+import { env, envNumber } from '@/global/env';
+
+export const MailerConfig = registerAs('mailer', () => ({
+ host: env('SMTP_HOST'),
+ port: envNumber('SMTP_PORT'),
+ ignoreTLS: true,
+ secure: true,
+ auth: {
+ user: env('SMTP_USER'),
+ pass: env('SMTP_PASS'),
+ },
+}));
+
+export type IMailerConfig = ConfigType;
diff --git a/apps/api/src/config/modules/app.config.ts b/apps/api/src/config/modules/app.config.ts
deleted file mode 100644
index a2d3001..0000000
--- a/apps/api/src/config/modules/app.config.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { registerAs } from '@nestjs/config';
-
-import { env, envNumber } from '@/config/env';
-
-export const app = registerAs('app', () => ({
- name: env('APP_NAME'),
- port: envNumber('PORT', 3000),
- globalPrefix: env('GLOBAL_PREFIX'),
- adminRoleId: envNumber('ADMIN_ROLE_ID'),
- userPwdSalt: env('USER_PWD_SALT'),
- userDefaultPwd: env('USER_DEFAULT_PWD'),
- protectSysPermMenuMaxId: envNumber('PROTECT_SYS_PERMMENU_MAX_ID'),
- protectSysDictionaryMaxId: envNumber('PROTECT_SYS_DICTIONARY_MAX_ID'),
- locale: env('APP_LOCALE', 'zh-CN'),
-
- logger: {
- level: env('LOGGER_LEVEL'),
- maxFiles: envNumber('LOGGER_MAX_FILES'),
- },
-}));
-
-export type IAppConfig = ReturnType;
diff --git a/apps/api/src/config/modules/jwt.config.ts b/apps/api/src/config/modules/jwt.config.ts
deleted file mode 100644
index 5c548ca..0000000
--- a/apps/api/src/config/modules/jwt.config.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { registerAs } from '@nestjs/config';
-
-import { env, envNumber } from '@/config/env';
-
-export const jwt = registerAs('jwt', () => ({
- secret: env('JWT_SECRET'),
- expires: envNumber('JWT_EXPIRES'),
- refreshSecret: env('REFRESH_TOKEN_SECRET'),
- refreshExpires: envNumber('REFRESH_TOKEN_EXPIRES'),
-}));
-
-export type IJwtConfig = ReturnType;
diff --git a/apps/api/src/config/modules/mailer.config.ts b/apps/api/src/config/modules/mailer.config.ts
deleted file mode 100644
index db661e4..0000000
--- a/apps/api/src/config/modules/mailer.config.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { registerAs } from '@nestjs/config';
-
-import { env, envNumber } from '@/config/env';
-
-export const mailer = registerAs('mailer', () => ({
- host: env('EMAIL_HOST'),
- port: envNumber('EMAIL_PORT'),
- ignoreTLS: true,
- secure: true,
- auth: {
- user: env('EMAIL_USER'),
- pass: env('EMAIL_PASS'),
- },
-}));
-
-export type IMailerConfig = ReturnType;
diff --git a/apps/api/src/config/modules/redis.config.ts b/apps/api/src/config/modules/redis.config.ts
deleted file mode 100644
index 81aa02f..0000000
--- a/apps/api/src/config/modules/redis.config.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { registerAs } from '@nestjs/config';
-
-import { env, envNumber } from '@/config/env';
-
-export const redis = registerAs('redis', () => ({
- host: env('REDIS_HOST', '127.0.0.1'),
- port: envNumber('REDIS_PORT', 6379),
- password: env('REDIS_PASSWORD'),
- db: envNumber('REDIS_DB'),
-}));
-
-export type IRedisConfig = ReturnType;
diff --git a/apps/api/src/config/modules/swagger.config.ts b/apps/api/src/config/modules/swagger.config.ts
deleted file mode 100644
index 80ac24c..0000000
--- a/apps/api/src/config/modules/swagger.config.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { registerAs } from '@nestjs/config';
-
-import { env, envBoolean } from '@/config/env';
-
-export const swagger = registerAs('swagger', () => ({
- enable: envBoolean('SWAGGER_ENABLE'),
- path: env('SWAGGER_PATH'),
-}));
-
-export type ISwaggerConfig = ReturnType;
diff --git a/apps/api/src/config/redis.config.ts b/apps/api/src/config/redis.config.ts
new file mode 100644
index 0000000..f5b2eb8
--- /dev/null
+++ b/apps/api/src/config/redis.config.ts
@@ -0,0 +1,12 @@
+import { ConfigType, registerAs } from '@nestjs/config';
+
+import { env, envNumber } from '@/global/env';
+
+export const RedisConfig = registerAs('redis', () => ({
+ host: env('REDIS_HOST', '127.0.0.1'),
+ port: envNumber('REDIS_PORT', 6379),
+ password: env('REDIS_PASSWORD'),
+ db: envNumber('REDIS_DB'),
+}));
+
+export type IRedisConfig = ConfigType;
diff --git a/apps/api/src/config/security.config.ts b/apps/api/src/config/security.config.ts
new file mode 100644
index 0000000..3a7df7d
--- /dev/null
+++ b/apps/api/src/config/security.config.ts
@@ -0,0 +1,12 @@
+import { ConfigType, registerAs } from '@nestjs/config';
+
+import { env, envNumber } from '@/global/env';
+
+export const SecurityConfig = registerAs('security', () => ({
+ jwtSecret: env('JWT_SECRET'),
+ jwtExprire: envNumber('JWT_EXPIRE'),
+ refreshSecret: env('REFRESH_TOKEN_SECRET'),
+ refreshExpire: envNumber('REFRESH_TOKEN_EXPIRE'),
+}));
+
+export type ISecurityConfig = ConfigType;
diff --git a/apps/api/src/config/modules/sms.config.ts b/apps/api/src/config/sms.config.ts
similarity index 54%
rename from apps/api/src/config/modules/sms.config.ts
rename to apps/api/src/config/sms.config.ts
index b3140a8..2037a19 100644
--- a/apps/api/src/config/modules/sms.config.ts
+++ b/apps/api/src/config/sms.config.ts
@@ -1,8 +1,8 @@
-import { registerAs } from '@nestjs/config';
+import { ConfigType, registerAs } from '@nestjs/config';
-import { env } from '@/config/env';
+import { env } from '@/global/env';
-export const sms = registerAs('sms', () => ({
+export const SmsConfig = registerAs('sms', () => ({
sign: env('SMS_SING', 'Youni'),
region: env('SMS_REGION', 'ap-guangzhou'),
appid: env('SMS_APPID', '1400437232'),
@@ -10,4 +10,4 @@ export const sms = registerAs('sms', () => ({
secretKey: env('SMS_SECRET_KEY', 'your-secret-key'),
}));
-export type ISmsConfig = ReturnType;
+export type ISmsConfig = ConfigType;
diff --git a/apps/api/src/config/swagger.config.ts b/apps/api/src/config/swagger.config.ts
new file mode 100644
index 0000000..7cdd705
--- /dev/null
+++ b/apps/api/src/config/swagger.config.ts
@@ -0,0 +1,10 @@
+import { ConfigType, registerAs } from '@nestjs/config';
+
+import { env, envBoolean } from '@/global/env';
+
+export const SwaggerConfig = registerAs('swagger', () => ({
+ enable: envBoolean('SWAGGER_ENABLE'),
+ path: env('SWAGGER_PATH'),
+}));
+
+export type ISwaggerConfig = ConfigType;
diff --git a/apps/api/src/decorators/index.ts b/apps/api/src/decorators/index.ts
deleted file mode 100644
index dda21d2..0000000
--- a/apps/api/src/decorators/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './api-result.decorator';
-export * from './api-token.decorator';
-export * from './http.decorator';
-export * from './skip-transform.decorator';
diff --git a/apps/api/src/filters/app.filter.ts b/apps/api/src/filters/app.filter.ts
deleted file mode 100644
index 4fe663a..0000000
--- a/apps/api/src/filters/app.filter.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import {
- ArgumentsHost,
- Catch,
- ExceptionFilter,
- HttpException,
- HttpStatus,
- Logger,
-} from '@nestjs/common';
-import { FastifyReply } from 'fastify';
-
-import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiException } from '@/exceptions/api.exception';
-import { isDev } from '@/global/env';
-
-@Catch()
-export class AppFilter implements ExceptionFilter {
- private readonly logger = new Logger(AppFilter.name);
-
- constructor() {
- this.registerCatchAllExceptionsHook();
- }
-
- catch(exception: unknown, host: ArgumentsHost) {
- this.logger.error(exception);
-
- const ctx = host.switchToHttp();
- const response = ctx.getResponse();
-
- // 响应结果码判断
- const httpStatus: number =
- exception instanceof HttpException
- ? exception.getStatus()
- : HttpStatus.INTERNAL_SERVER_ERROR;
-
- const apiErrorCode: number =
- exception instanceof ApiException ? exception.getErrorCode() : httpStatus;
-
- let errorMessage: string =
- exception instanceof HttpException ? exception.message : `${exception}`;
-
- // 系统内部错误时,在生产模式下隐藏具体异常消息
- if (!isDev && httpStatus === HttpStatus.INTERNAL_SERVER_ERROR) {
- errorMessage = ErrorEnum.SERVER_ERROR?.split(':')[1];
- }
-
- // 返回基础响应结果
- const resBody: IBaseResponse = {
- code: apiErrorCode,
- message: errorMessage,
- data: null,
- };
-
- response.status(httpStatus).send(resBody);
- }
-
- registerCatchAllExceptionsHook() {
- process.on('unhandledRejection', (reason: any) => {
- console.error('unhandledRejection: ', reason);
- });
-
- process.on('uncaughtException', (err) => {
- console.error('uncaughtException: ', err);
- });
- }
-}
diff --git a/apps/api/src/global/env.ts b/apps/api/src/global/env.ts
index d5914f1..66d6f3d 100644
--- a/apps/api/src/global/env.ts
+++ b/apps/api/src/global/env.ts
@@ -9,3 +9,53 @@ export const isDev = process.env.NODE_ENV === 'development';
export const isTest = !!process.env.TEST;
export const cwd = process.cwd();
+
+/**
+ * 基础类型接口
+ */
+export type BaseType = boolean | number | string | undefined | null;
+
+/**
+ * 格式化环境变量
+ * @param key 环境变量的键值
+ * @param defaultValue 默认值
+ * @param callback 格式化函数
+ */
+const fromatValue = (
+ key: string,
+ defaultValue: T,
+ callback?: (value: string) => T,
+): T => {
+ const value: string | undefined = process.env[key];
+ if (typeof value === 'undefined') {
+ return defaultValue;
+ }
+ if (!callback) {
+ return value as unknown as T;
+ }
+ return callback(value);
+};
+
+export const env = (key: string, defaultValue: string = '') =>
+ fromatValue(key, defaultValue);
+
+export const envString = (key: string, defaultValue: string = '') =>
+ fromatValue(key, defaultValue);
+
+export const envNumber = (key: string, defaultValue: number = 0) =>
+ fromatValue(key, defaultValue, (value) => {
+ try {
+ return Number(value);
+ } catch {
+ throw new Error(`${key} environment variable is not a number`);
+ }
+ });
+
+export const envBoolean = (key: string, defaultValue: boolean = false) =>
+ fromatValue(key, defaultValue, (value) => {
+ try {
+ return Boolean(JSON.parse(value));
+ } catch {
+ throw new Error(`${key} environment variable is not a boolean`);
+ }
+ });
diff --git a/apps/api/src/magrations/1689457720509-UpdateTodo.ts b/apps/api/src/magrations/1689457720509-UpdateTodo.ts
deleted file mode 100644
index c4c82a9..0000000
--- a/apps/api/src/magrations/1689457720509-UpdateTodo.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { MigrationInterface, QueryRunner } from 'typeorm';
-
-export class UpdateTodo1689457720509 implements MigrationInterface {
- name = 'UpdateTodo1689457720509';
-
- public async up(queryRunner: QueryRunner): Promise {
- await queryRunner.query(`ALTER TABLE \`todo\` DROP COLUMN \`status\``);
- await queryRunner.query(
- `ALTER TABLE \`todo\` ADD \`status\` tinyint NOT NULL DEFAULT 0`,
- );
- }
-
- public async down(queryRunner: QueryRunner): Promise {
- await queryRunner.query(`ALTER TABLE \`todo\` DROP COLUMN \`status\``);
- await queryRunner.query(
- `ALTER TABLE \`todo\` ADD \`status\` varchar(255) NOT NULL DEFAULT '0'`,
- );
- }
-}
diff --git a/apps/api/src/main.ts b/apps/api/src/main.ts
index 5b8fb90..edd21a6 100644
--- a/apps/api/src/main.ts
+++ b/apps/api/src/main.ts
@@ -1,6 +1,5 @@
import cluster from 'cluster';
import path from 'path';
-import { performance } from 'perf_hooks';
import {
ClassSerializerInterceptor,
@@ -11,78 +10,55 @@ import {
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { NestFactory, Reflector } from '@nestjs/core';
-import {
- NestFastifyApplication,
- FastifyAdapter,
-} from '@nestjs/platform-fastify';
-
-import { IoAdapter } from '@nestjs/platform-socket.io';
+import { NestFastifyApplication } from '@nestjs/platform-fastify';
import { useContainer } from 'class-validator';
import { AppModule } from './app.module';
+import { fastifyApp } from './common/adapters/fastify.adapter';
+import { IoAdapter } from './common/adapters/socket.adapter';
+import { LoggingInterceptor } from './common/interceptors/logging.interceptor';
+import { TimeoutInterceptor } from './common/interceptors/timeout.interceptor';
+import { TransformInterceptor } from './common/interceptors/transform.interceptor';
import { IAppConfig } from './config';
-import { AppFilter } from './filters/app.filter';
import { isDev, isMainProcess } from './global/env';
-import { LoggingInterceptor } from './interceptors/logging.interceptor';
-import { TimeoutInterceptor } from './interceptors/timeout.interceptor';
-import { TransformInterceptor } from './interceptors/transform.interceptor';
-import { AppLoggerService } from './modules/shared/services/app-logger.service';
-import { setupSwagger } from './utils/setup-swagger';
-
-// catchError();
+import { setupSwagger } from './setup-swagger';
+import { MyLogger } from './shared/logger/logger.service';
declare const module: any;
async function bootstrap() {
const app = await NestFactory.create(
AppModule,
- new FastifyAdapter(),
+ fastifyApp,
{
bufferLogs: true,
snapshot: true,
},
);
- // app config service
const configService = app.get(ConfigService);
- // reflector
const reflector = app.get(Reflector);
- // class-validator 的 DTO 类中注入nestjs容器的依赖
+ // class-validator 的 DTO 类中注入 nest 容器的依赖
useContainer(app.select(AppModule), { fallbackOnErrors: true });
- app.useLogger(app.get(AppLoggerService));
app.enableCors({ origin: '*', credentials: true });
+ app.setGlobalPrefix('api');
app.useStaticAssets({ root: path.join(__dirname, '..', 'public') });
- // https://github.com/fastify/fastify-multipart/
- // eslint-disable-next-line global-require
- await app.register(require('@fastify/multipart'), {
- attachFieldsToBody: true,
- limits: {
- fileSize: 1024 * 1024 * 10, // 10M
- files: 1,
- },
- });
-
- // 处理异常请求
- app.useGlobalFilters(new AppFilter());
-
app.useGlobalInterceptors(
- // 请求超时
- new TimeoutInterceptor(30000),
- // 序列化
new ClassSerializerInterceptor(reflector),
- // Logging
- isDev ? new LoggingInterceptor() : null,
- // 返回数据转换
- new TransformInterceptor(new Reflector()),
+ new TransformInterceptor(reflector),
+ new TimeoutInterceptor(),
);
- // 使用全局管道验证数据
+ if (isDev) {
+ app.useGlobalInterceptors(new LoggingInterceptor());
+ }
+
app.useGlobalPipes(
new ValidationPipe({
transform: true,
@@ -90,12 +66,12 @@ async function bootstrap() {
transformOptions: { enableImplicitConversion: true },
// forbidNonWhitelisted: true, // 禁止 无装饰器验证的数据通过
errorHttpStatusCode: HttpStatus.UNPROCESSABLE_ENTITY,
+ stopAtFirstError: true,
exceptionFactory: (errors) =>
new UnprocessableEntityException(
errors.map((e) => {
- const rule = Object.keys(e.constraints)[0];
- const msg = e.constraints[rule];
- // return `property ${e.property} validation failed: ${msg}, following constraints: ${rule}`;
+ const rule = Object.keys(e.constraints!)[0];
+ const msg = e.constraints![rule];
return msg;
})[0],
),
@@ -105,13 +81,12 @@ async function bootstrap() {
// websocket
app.useWebSocketAdapter(new IoAdapter());
- // global prefix
- const { globalPrefix, port } = configService.get('app');
- app.setGlobalPrefix(globalPrefix);
+ const { port } = configService.get('app')!;
setupSwagger(app, configService);
await app.listen(port, '0.0.0.0', async () => {
+ app.useLogger(app.get(MyLogger));
const url = await app.getUrl();
const { pid } = process;
const env = cluster.isPrimary;
@@ -125,9 +100,8 @@ async function bootstrap() {
logger.log(`[${prefix + pid}] Server running on ${url}`);
if (isDev) {
- logger.log(`[${prefix + pid}] OpenApi: ${url}/api-docs`);
+ logger.log(`[${prefix + pid}] OpenAPI: ${url}/api-docs`);
}
- logger.log(`Server is up. ${`+${performance.now() | 0}ms`}`);
});
if (module.hot) {
diff --git a/apps/api/src/modules/apps/apps.module.ts b/apps/api/src/modules/apps/apps.module.ts
deleted file mode 100644
index 4772c43..0000000
--- a/apps/api/src/modules/apps/apps.module.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Module, forwardRef } from '@nestjs/common';
-import { TypeOrmModule } from '@nestjs/typeorm';
-
-import { TodoEntity } from '@/modules/apps/todo/todo.entity';
-
-import { SystemModule } from '../system/system.module';
-
-import { TodoController } from './todo/todo.controller';
-import { TodoService } from './todo/todo.service';
-
-@Module({
- imports: [
- forwardRef(() => SystemModule),
- TypeOrmModule.forFeature([TodoEntity]),
- ],
- controllers: [TodoController],
- providers: [TodoService],
- exports: [TypeOrmModule],
-})
-export class AppsModule {}
diff --git a/apps/api/src/modules/apps/todo/todo.permission.ts b/apps/api/src/modules/apps/todo/todo.permission.ts
deleted file mode 100644
index fff85f6..0000000
--- a/apps/api/src/modules/apps/todo/todo.permission.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export const PermissionTodo = {
- LIST: 'todo:list',
- CREATE: 'todo:create',
- READ: 'todo:read',
- UPDATE: 'todo:update',
- DELETE: 'todo:delete',
-} as const;
diff --git a/apps/api/src/modules/auth/auth.controller.ts b/apps/api/src/modules/auth/auth.controller.ts
index 96df6b5..ca59068 100644
--- a/apps/api/src/modules/auth/auth.controller.ts
+++ b/apps/api/src/modules/auth/auth.controller.ts
@@ -1,13 +1,13 @@
import { Body, Controller, Headers, Post, UseGuards } from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
-import { Ip } from '@/decorators';
-import { ApiResult } from '@/decorators/api-result.decorator';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
+import { Ip } from '@/common/decorators/http.decorator';
-import { UserService } from '../system/user/user.service';
+import { UserService } from '../user/user.service';
import { AuthService } from './auth.service';
-import { Public } from './decorators';
+import { Public } from './decorators/public.decorator';
import { LoginDto, RegisterDto } from './dto/auth.dto';
import { LocalGuard } from './guards/local.guard';
import { LoginToken } from './models/auth.model';
diff --git a/apps/api/src/modules/auth/auth.module.ts b/apps/api/src/modules/auth/auth.module.ts
index 7cb81d2..eb6d921 100644
--- a/apps/api/src/modules/auth/auth.module.ts
+++ b/apps/api/src/modules/auth/auth.module.ts
@@ -5,13 +5,13 @@ import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
-import { IJwtConfig } from '@/config';
+import { ISecurityConfig } from '@/config';
import { isDev } from '@/global/env';
import { LogModule } from '../system/log/log.module';
import { MenuModule } from '../system/menu/menu.module';
import { RoleModule } from '../system/role/role.module';
-import { UserModule } from '../system/user/user.module';
+import { UserModule } from '../user/user.module';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
@@ -41,11 +41,12 @@ const strategies = [LocalStrategy, JwtStrategy];
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => {
- const { secret, expires } = configService.get('jwt');
+ const { jwtSecret, jwtExprire } =
+ configService.get('security');
return {
- secret,
- expires,
+ secret: jwtSecret,
+ expires: jwtExprire,
ignoreExpiration: isDev,
};
},
diff --git a/apps/api/src/modules/auth/auth.service.ts b/apps/api/src/modules/auth/auth.service.ts
index c846f39..44c7499 100644
--- a/apps/api/src/modules/auth/auth.service.ts
+++ b/apps/api/src/modules/auth/auth.service.ts
@@ -4,10 +4,10 @@ import { Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import { isEmpty } from 'lodash';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiException } from '@/exceptions/api.exception';
-import { UserService } from '@/modules/system/user/user.service';
+import { UserService } from '@/modules/user/user.service';
import { MD5 } from '@/utils';
@@ -32,12 +32,12 @@ export class AuthService {
const user = await this.userService.findUserByUserName(credential);
if (isEmpty(user)) {
- throw new ApiException(ErrorEnum.USER_NOT_FOUND);
+ throw new BusinessException(ErrorEnum.USER_NOT_FOUND);
}
const comparePassword = MD5(`${password}${user.psalt}`);
if (user.password !== comparePassword) {
- throw new ApiException(ErrorEnum.PASSWORD_MISMATCH);
+ throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD);
}
if (user) {
@@ -60,12 +60,12 @@ export class AuthService {
): Promise {
const user = await this.userService.findUserByUserName(username);
if (isEmpty(user)) {
- throw new ApiException(ErrorEnum.USER_NOT_FOUND);
+ throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD);
}
const comparePassword = MD5(`${password}${user.psalt}`);
if (user.password !== comparePassword) {
- throw new ApiException(ErrorEnum.PASSWORD_MISMATCH);
+ throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD);
}
const roleIds = await this.roleService.getRoleIdsByUser(user.id);
@@ -82,7 +82,7 @@ export class AuthService {
// 设置菜单权限
const permissions = await this.menuService.getPermissions(user.id);
- await this.setPermissions(user.id, permissions);
+ await this.setPermissionsCache(user.id, permissions);
await this.loginLogService.create(user.id, ip, ua);
@@ -97,7 +97,7 @@ export class AuthService {
const comparePassword = MD5(`${password}${user.psalt}`);
if (user.password !== comparePassword) {
- throw new ApiException(ErrorEnum.PASSWORD_MISMATCH);
+ throw new BusinessException(ErrorEnum.INVALID_USERNAME_PASSWORD);
}
}
@@ -140,7 +140,12 @@ export class AuthService {
return this.menuService.getPermissions(uid);
}
- async setPermissions(uid: number, permissions: string[]): Promise {
+ async getPermissionsCache(uid: number): Promise {
+ const permissionString = await this.redis.get(`auth:permission:${uid}`);
+ return permissionString ? JSON.parse(permissionString) : [];
+ }
+
+ async setPermissionsCache(uid: number, permissions: string[]): Promise {
await this.redis.set(`auth:permission:${uid}`, JSON.stringify(permissions));
}
@@ -151,9 +156,4 @@ export class AuthService {
async getTokenByUid(uid: number): Promise {
return this.redis.get(`auth:token:${uid}`);
}
-
- async getPermissionsByUid(uid: number): Promise {
- const permissionString = await this.redis.get(`auth:permission:${uid}`);
- return permissionString ? JSON.parse(permissionString) : [];
- }
}
diff --git a/apps/api/src/modules/rbac/constant.ts b/apps/api/src/modules/auth/constant.ts
similarity index 52%
rename from apps/api/src/modules/rbac/constant.ts
rename to apps/api/src/modules/auth/constant.ts
index d98b5a6..fc69e65 100644
--- a/apps/api/src/modules/rbac/constant.ts
+++ b/apps/api/src/modules/auth/constant.ts
@@ -1,9 +1,24 @@
+export const IS_PUBLIC_KEY = 'is_public';
+
export const PERMISSION_KEY = 'permission';
export const POLICY_KEY = 'policy';
export const ALLOW_ANON_KEY = 'allow_anon_permission';
+export const AuthStrategy = {
+ LOCAL: 'local',
+ LOCAL_EMAIL: 'local_email',
+ LOCAL_PHONE: 'local_phone',
+
+ JWT: 'jwt',
+
+ GITHUB: 'github',
+ GOOGLE: 'google',
+
+ PDD: 'pdd',
+} as const;
+
export const Roles = {
ADMIN: 'admin',
USER: 'user',
diff --git a/apps/api/src/modules/auth/constants.ts b/apps/api/src/modules/auth/constants.ts
deleted file mode 100644
index b72a6be..0000000
--- a/apps/api/src/modules/auth/constants.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-export const IS_PUBLIC_KEY = 'is_public';
-
-export const AuthStrategy = {
- LOCAL: 'local',
- LOCAL_EMAIL: 'local_email',
- LOCAL_PHONE: 'local_phone',
-
- JWT: 'jwt',
-
- GITHUB: 'github',
-} as const;
diff --git a/apps/api/src/modules/auth/controllers/account.controller.ts b/apps/api/src/modules/auth/controllers/account.controller.ts
index 67ac5fc..3631b34 100644
--- a/apps/api/src/modules/auth/controllers/account.controller.ts
+++ b/apps/api/src/modules/auth/controllers/account.controller.ts
@@ -1,18 +1,18 @@
import { Body, Controller, Get, Post, Put, UseGuards } from '@nestjs/common';
import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger';
-import { ApiResult } from '@/decorators/api-result.decorator';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
-import { AuthUser } from '@/modules/auth/decorators';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
+import { AllowAnon } from '@/modules/auth/decorators/allow-anon.decorator';
+import { AuthUser } from '@/modules/auth/decorators/auth-user.decorator';
-import { AllowAnon } from '@/modules/rbac/decorators';
import { MenuEntity } from '@/modules/system/menu/menu.entity';
-import { PasswordUpdateDto } from '@/modules/system/user/dto/password.dto';
+import { PasswordUpdateDto } from '@/modules/user/dto/password.dto';
-import { AccountInfo } from '../../system/user/user.model';
-import { UserService } from '../../system/user/user.service';
+import { AccountInfo } from '../../user/user.model';
+import { UserService } from '../../user/user.service';
import { AuthService } from '../auth.service';
import { AccountUpdateDto } from '../dto/account.dto';
import { JwtAuthGuard } from '../guards/jwt-auth.guard';
diff --git a/apps/api/src/modules/auth/controllers/captcha.controller.ts b/apps/api/src/modules/auth/controllers/captcha.controller.ts
index 1e46adc..149324f 100644
--- a/apps/api/src/modules/auth/controllers/captcha.controller.ts
+++ b/apps/api/src/modules/auth/controllers/captcha.controller.ts
@@ -8,11 +8,11 @@ import Redis from 'ioredis';
import { isEmpty } from 'lodash';
import * as svgCaptcha from 'svg-captcha';
-import { ApiResult } from '@/decorators';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
import { generateUUID } from '@/utils';
-import { Public } from '../decorators';
+import { Public } from '../decorators/public.decorator';
import { ImageCaptchaDto } from '../dto/captcha.dto';
import { ImageCaptcha } from '../models/auth.model';
@@ -27,7 +27,7 @@ export class CaptchaController {
@ApiOperation({ summary: '获取登录图片验证码' })
@ApiResult({ type: ImageCaptcha })
@Public()
- @Throttle({ default: { limit: 2, ttl: 60000 } })
+ @Throttle({ default: { limit: 2, ttl: 600000 } })
async captchaByImg(@Query() dto: ImageCaptchaDto): Promise {
const { width, height } = dto;
diff --git a/apps/api/src/modules/auth/controllers/email.controller.ts b/apps/api/src/modules/auth/controllers/email.controller.ts
index 9bcda28..9eb4618 100644
--- a/apps/api/src/modules/auth/controllers/email.controller.ts
+++ b/apps/api/src/modules/auth/controllers/email.controller.ts
@@ -3,10 +3,11 @@ import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { Throttle, ThrottlerGuard } from '@nestjs/throttler';
-import { Ip } from '@/decorators/http.decorator';
-import { MailerService } from '@/modules/shared/mailer/mailer.service';
+import { Ip } from '@/common/decorators/http.decorator';
+import { MailerService } from '@/shared/mailer/mailer.service';
-import { AuthUser, Public } from '../decorators';
+import { AuthUser } from '../decorators/auth-user.decorator';
+import { Public } from '../decorators/public.decorator';
import { SendEmailCodeDto } from '../dto/captcha.dto';
@@ -19,7 +20,7 @@ export class EmailController {
@Post('send')
@ApiOperation({ summary: '发送邮箱验证码' })
@Public()
- @Throttle({ default: { limit: 2, ttl: 60000 } })
+ @Throttle({ default: { limit: 2, ttl: 600000 } })
async sendEmailCode(
@Body() dto: SendEmailCodeDto,
@Ip() ip: string,
diff --git a/apps/api/src/modules/rbac/decorators/allow-anon.decorator.ts b/apps/api/src/modules/auth/decorators/allow-anon.decorator.ts
similarity index 100%
rename from apps/api/src/modules/rbac/decorators/allow-anon.decorator.ts
rename to apps/api/src/modules/auth/decorators/allow-anon.decorator.ts
diff --git a/apps/api/src/modules/auth/decorators/index.ts b/apps/api/src/modules/auth/decorators/index.ts
deleted file mode 100644
index 3f71074..0000000
--- a/apps/api/src/modules/auth/decorators/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './auth-user.decorator';
-export * from './public.decorator';
diff --git a/apps/api/src/modules/rbac/decorators/permission.decorator.ts b/apps/api/src/modules/auth/decorators/permission.decorator.ts
similarity index 100%
rename from apps/api/src/modules/rbac/decorators/permission.decorator.ts
rename to apps/api/src/modules/auth/decorators/permission.decorator.ts
diff --git a/apps/api/src/modules/auth/decorators/public.decorator.ts b/apps/api/src/modules/auth/decorators/public.decorator.ts
index e5cf56e..c402d38 100644
--- a/apps/api/src/modules/auth/decorators/public.decorator.ts
+++ b/apps/api/src/modules/auth/decorators/public.decorator.ts
@@ -1,6 +1,6 @@
import { SetMetadata } from '@nestjs/common';
-import { IS_PUBLIC_KEY } from '../constants';
+import { IS_PUBLIC_KEY } from '../constant';
/**
* 当接口不需要检测用户登录时添加该装饰器
diff --git a/apps/api/src/modules/rbac/decorators/resource.decorator.ts b/apps/api/src/modules/auth/decorators/resource.decorator.ts
similarity index 100%
rename from apps/api/src/modules/rbac/decorators/resource.decorator.ts
rename to apps/api/src/modules/auth/decorators/resource.decorator.ts
diff --git a/apps/api/src/modules/auth/entities/access-token.entity.ts b/apps/api/src/modules/auth/entities/access-token.entity.ts
index eafa9af..4dd0a6c 100644
--- a/apps/api/src/modules/auth/entities/access-token.entity.ts
+++ b/apps/api/src/modules/auth/entities/access-token.entity.ts
@@ -8,7 +8,7 @@ import {
PrimaryGeneratedColumn,
} from 'typeorm';
-import { UserEntity } from '@/modules/system/user/entities/user.entity';
+import { UserEntity } from '@/modules/user/entities/user.entity';
import { RefreshTokenEntity } from './refresh-token.entity';
diff --git a/apps/api/src/modules/auth/entities/index.ts b/apps/api/src/modules/auth/entities/index.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/apps/api/src/modules/auth/guards/jwt-auth.guard.ts b/apps/api/src/modules/auth/guards/jwt-auth.guard.ts
index a645403..f222bc1 100644
--- a/apps/api/src/modules/auth/guards/jwt-auth.guard.ts
+++ b/apps/api/src/modules/auth/guards/jwt-auth.guard.ts
@@ -1,15 +1,19 @@
-import { ExecutionContext, Injectable } from '@nestjs/common';
+import {
+ ExecutionContext,
+ Injectable,
+ UnauthorizedException,
+} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthGuard } from '@nestjs/passport';
import { FastifyReply, FastifyRequest } from 'fastify';
import { isEmpty, isNil } from 'lodash';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiException } from '@/exceptions/api.exception';
import { AuthService } from '@/modules/auth/auth.service';
import { TokenService } from '@/modules/auth/services/token.service';
-import { AuthStrategy, IS_PUBLIC_KEY } from '../constants';
+import { AuthStrategy, IS_PUBLIC_KEY } from '../constant';
@Injectable()
export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) {
@@ -22,7 +26,6 @@ export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) {
}
async canActivate(context: ExecutionContext): Promise {
- // 检测是否是开放类型的,例如获取验证码类型的接口不需要校验,可以加入@Public可自动放过
const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
@@ -31,62 +34,36 @@ export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) {
const request = context.switchToHttp().getRequest();
const response = context.switchToHttp().getResponse();
- let requestToken = request.headers.authorization;
+ const Authorization = request.headers.authorization;
let result: any = false;
try {
result = await super.canActivate(context);
} catch (e) {
+ // 需要后置判断 这样携带了 token 的用户就能够解析到 request.user
if (isPublic) return true;
- if (isEmpty(requestToken)) {
- throw new ApiException(ErrorEnum.INVALID_LOGIN);
+ if (isEmpty(Authorization)) {
+ throw new UnauthorizedException('未登录');
}
- // 判断token是否存在,如果不存在则认证失败
- const accessToken = isNil(requestToken)
+ // 判断 token 是否存在, 如果不存在则认证失败
+ const accessToken = isNil(Authorization)
? undefined
- : await this.tokenService.checkAccessToken(requestToken!);
- if (!accessToken) throw new ApiException(ErrorEnum.INVALID_LOGIN);
+ : await this.tokenService.checkAccessToken(Authorization!);
- // 无法通过token校验
- // 尝试通过refreshToken刷新token
-
- if (!isNil(requestToken)) {
- const token = await this.tokenService.refreshToken(accessToken);
- if (isNil(token)) throw new ApiException(ErrorEnum.INVALID_LOGIN);
-
- if (token.accessToken) {
- // 将新的token挂载到当前请求上
- requestToken = token.accessToken;
-
- response.header('new-token', token.accessToken);
- }
-
- try {
- // 刷新失败(refreshToken过期)则再次抛出认证失败的异常
- result = await super.canActivate(context);
- } catch (error) {
- throw new ApiException(ErrorEnum.INVALID_LOGIN);
- }
- }
- }
-
- if (isPublic) return true;
-
- if (isEmpty(request.user)) {
- throw new ApiException(ErrorEnum.INVALID_LOGIN);
+ if (!accessToken) throw new UnauthorizedException('令牌无效');
}
const pv = await this.authService.getPasswordVersionByUid(request.user.uid);
if (pv !== `${request.user.pv}`) {
// 密码版本不一致,登录期间已更改过密码
- throw new ApiException(ErrorEnum.INVALID_LOGIN);
+ throw new BusinessException(ErrorEnum.INVALID_LOGIN);
}
// 不允许多端登录
// const cacheToken = await this.authService.getTokenByUid(request.user.uid);
- // if (requestToken !== cacheToken) {
+ // if (Authorization !== cacheToken) {
// // 与redis保存不一致 即二次登录
// throw new ApiException(ErrorEnum.CODE_1106);
// }
@@ -97,7 +74,7 @@ export class JwtAuthGuard extends AuthGuard(AuthStrategy.JWT) {
handleRequest(err, user, info) {
// You can throw an exception based on either "info" or "err" arguments
if (err || !user) {
- throw err;
+ throw err || new UnauthorizedException();
}
return user;
}
diff --git a/apps/api/src/modules/auth/guards/local.guard.ts b/apps/api/src/modules/auth/guards/local.guard.ts
index 898e8a4..028a4f1 100644
--- a/apps/api/src/modules/auth/guards/local.guard.ts
+++ b/apps/api/src/modules/auth/guards/local.guard.ts
@@ -1,7 +1,7 @@
import { ExecutionContext, Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
-import { AuthStrategy } from '../constants';
+import { AuthStrategy } from '../constant';
@Injectable()
export class LocalGuard extends AuthGuard(AuthStrategy.LOCAL) {
diff --git a/apps/api/src/modules/rbac/guards/rbac.guard.ts b/apps/api/src/modules/auth/guards/rbac.guard.ts
similarity index 66%
rename from apps/api/src/modules/rbac/guards/rbac.guard.ts
rename to apps/api/src/modules/auth/guards/rbac.guard.ts
index 95104f8..d034c36 100644
--- a/apps/api/src/modules/rbac/guards/rbac.guard.ts
+++ b/apps/api/src/modules/auth/guards/rbac.guard.ts
@@ -1,22 +1,22 @@
-import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
+import {
+ CanActivate,
+ ExecutionContext,
+ Injectable,
+ UnauthorizedException,
+} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { FastifyRequest } from 'fastify';
-import { DataSource } from 'typeorm';
-
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiException } from '@/exceptions/api.exception';
import { AuthService } from '@/modules/auth/auth.service';
-import { IS_PUBLIC_KEY } from '../../auth/constants';
-
-import { ALLOW_ANON_KEY, PERMISSION_KEY } from '../constant';
+import { ALLOW_ANON_KEY, PERMISSION_KEY, IS_PUBLIC_KEY } from '../constant';
@Injectable()
export class RbacGuard implements CanActivate {
constructor(
private reflector: Reflector,
- private dataSource: DataSource,
private authService: AuthService,
) {}
@@ -31,34 +31,34 @@ export class RbacGuard implements CanActivate {
const request = context.switchToHttp().getRequest();
const { user } = request;
+ if (!user) throw new UnauthorizedException('登录无效');
- if (!user) return false;
-
+ // allowAnon 是需要登录后可访问(无需权限), Public 则是无需登录也可访问.
const allowAnon = this.reflector.get(
ALLOW_ANON_KEY,
context.getHandler(),
);
- // Token校验身份通过,判断是否需要权限的url,不需要权限则pass
if (allowAnon) return true;
const payloadPermission = this.reflector.getAllAndOverride<
string | string[]
>(PERMISSION_KEY, [context.getHandler(), context.getClass()]);
- let allPermissions = await this.authService.getPermissionsByUid(user.uid);
+ // 控制器没有设置接口权限,则默认通过
+ if (!payloadPermission) return true;
+ let allPermissions = await this.authService.getPermissionsCache(user.uid);
+
+ // 缓存失效, 则获取新的 Permission
if (!allPermissions) {
const res = await this.authService.getPermissions(user.uid);
allPermissions = res;
// set permissions into cache
- await this.authService.setPermissions(user.uid, allPermissions);
+ await this.authService.setPermissionsCache(user.uid, allPermissions);
}
- // 如果没有设置接口权限,则默认通过
- // if (isEmpty(payloadPermission)) return true;
-
let canNext = false;
// handle permission strings
@@ -72,7 +72,7 @@ export class RbacGuard implements CanActivate {
}
if (!canNext) {
- throw new ApiException(ErrorEnum.NO_PERMISSION);
+ throw new BusinessException(ErrorEnum.NO_PERMISSION);
}
return true;
diff --git a/apps/api/src/modules/rbac/guards/resource.guard.ts b/apps/api/src/modules/auth/guards/resource.guard.ts
similarity index 83%
rename from apps/api/src/modules/rbac/guards/resource.guard.ts
rename to apps/api/src/modules/auth/guards/resource.guard.ts
index fc2125a..6ef51cd 100644
--- a/apps/api/src/modules/rbac/guards/resource.guard.ts
+++ b/apps/api/src/modules/auth/guards/resource.guard.ts
@@ -6,12 +6,11 @@ import { isNil } from 'lodash';
import { DataSource, Repository } from 'typeorm';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiException } from '@/exceptions/api.exception';
-import { IS_PUBLIC_KEY } from '../../auth/constants';
+import { POLICY_KEY, Roles, IS_PUBLIC_KEY } from '../constant';
-import { POLICY_KEY, Roles } from '../constant';
import { ResourceObject } from '../decorators/resource.decorator';
@Injectable()
@@ -59,16 +58,16 @@ export class ResourceGuard implements CanActivate {
const id = getRequestItemId(request);
if (!id) {
- throw new ApiException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND);
+ throw new BusinessException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND);
}
const item = await repo.findOne({ where: { id }, relations: ['user'] });
if (!item) {
- throw new ApiException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND);
+ throw new BusinessException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND);
}
if (!item?.user) {
- throw new ApiException(ErrorEnum.USER_NOT_FOUND);
+ throw new BusinessException(ErrorEnum.USER_NOT_FOUND);
}
if (condition) {
@@ -77,7 +76,7 @@ export class ResourceGuard implements CanActivate {
// 如果没有设置policy,则默认只能操作自己的数据
if (item.user?.id !== user.uid) {
- throw new ApiException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND);
+ throw new BusinessException(ErrorEnum.REQUESTED_RESOURCE_NOT_FOUND);
}
}
diff --git a/apps/api/src/modules/auth/services/captcha.service.ts b/apps/api/src/modules/auth/services/captcha.service.ts
index 2017b78..1732c72 100644
--- a/apps/api/src/modules/auth/services/captcha.service.ts
+++ b/apps/api/src/modules/auth/services/captcha.service.ts
@@ -4,8 +4,8 @@ import { Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import { isEmpty } from 'lodash';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiException } from '@/exceptions/api.exception';
import { CaptchaLogService } from '@/modules/system/log/services/captcha-log.service';
@Injectable()
@@ -22,7 +22,7 @@ export class CaptchaService {
async checkImgCaptcha(id: string, code: string): Promise {
const result = await this.redis.get(`captcha:img:${id}`);
if (isEmpty(result) || code.toLowerCase() !== result.toLowerCase()) {
- throw new ApiException(ErrorEnum.INVALID_VERIFICATION_CODE);
+ throw new BusinessException(ErrorEnum.INVALID_VERIFICATION_CODE);
}
// 校验成功后移除验证码
await this.redis.del(`captcha:img:${id}`);
diff --git a/apps/api/src/modules/auth/services/token.service.ts b/apps/api/src/modules/auth/services/token.service.ts
index d68f8e4..d8dc8df 100644
--- a/apps/api/src/modules/auth/services/token.service.ts
+++ b/apps/api/src/modules/auth/services/token.service.ts
@@ -1,12 +1,12 @@
-import { Injectable } from '@nestjs/common';
-import { ConfigService } from '@nestjs/config';
+import { Inject, Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import dayjs from 'dayjs';
+import { ISecurityConfig, SecurityConfig } from '@/config';
import { RoleService } from '@/modules/system/role/role.service';
-import { UserEntity } from '@/modules/system/user/entities/user.entity';
+import { UserEntity } from '@/modules/user/entities/user.entity';
-import { generateUUID } from '@/utils';
+import { generateUUID } from '@/utils/uuid';
import { AccessTokenEntity } from '../entities/access-token.entity';
import { RefreshTokenEntity } from '../entities/refresh-token.entity';
@@ -18,8 +18,8 @@ import { RefreshTokenEntity } from '../entities/refresh-token.entity';
export class TokenService {
constructor(
private jwtService: JwtService,
- private configService: ConfigService,
private roleService: RoleService,
+ @Inject(SecurityConfig.KEY) private securityConfig: ISecurityConfig,
) {}
/**
@@ -47,6 +47,12 @@ export class TokenService {
return null;
}
+ generateJwtSign(payload: any) {
+ const jwtSign = this.jwtService.sign(payload);
+
+ return jwtSign;
+ }
+
async generateAccessToken(uid: number, roles: string[] = []) {
const payload: IAuthUser = {
uid,
@@ -61,7 +67,7 @@ export class TokenService {
accessToken.value = jwtSign;
accessToken.user = { id: uid } as UserEntity;
accessToken.expired_at = dayjs()
- .add(this.configService.get('jwt.expires'), 'second')
+ .add(this.securityConfig.jwtExprire, 'second')
.toDate();
await accessToken.save();
@@ -89,13 +95,13 @@ export class TokenService {
};
const refreshTokenSign = this.jwtService.sign(refreshTokenPayload, {
- secret: this.configService.get('jwt.refreshSecret'),
+ secret: this.securityConfig.refreshSecret,
});
const refreshToken = new RefreshTokenEntity();
refreshToken.value = refreshTokenSign;
refreshToken.expired_at = now
- .add(this.configService.get('jwt.refreshExpires'), 'second')
+ .add(this.securityConfig.refreshExpire, 'second')
.toDate();
refreshToken.accessToken = accessToken;
diff --git a/apps/api/src/modules/auth/strategies/jwt.strategy.ts b/apps/api/src/modules/auth/strategies/jwt.strategy.ts
index e1cbde1..2d9c230 100644
--- a/apps/api/src/modules/auth/strategies/jwt.strategy.ts
+++ b/apps/api/src/modules/auth/strategies/jwt.strategy.ts
@@ -1,19 +1,20 @@
-import { Injectable } from '@nestjs/common';
-import { ConfigService } from '@nestjs/config';
+import { Inject, Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
-import { IJwtConfig } from '@/config';
+import { ISecurityConfig, SecurityConfig } from '@/config';
-import { AuthStrategy } from '../constants';
+import { AuthStrategy } from '../constant';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, AuthStrategy.JWT) {
- constructor(private configService: ConfigService) {
+ constructor(
+ @Inject(SecurityConfig.KEY) private securityConfig: ISecurityConfig,
+ ) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
- secretOrKey: configService.get('jwt').secret,
+ secretOrKey: securityConfig.jwtSecret,
});
}
diff --git a/apps/api/src/modules/auth/strategies/local.strategy.ts b/apps/api/src/modules/auth/strategies/local.strategy.ts
index 4d2566c..acfdfe1 100644
--- a/apps/api/src/modules/auth/strategies/local.strategy.ts
+++ b/apps/api/src/modules/auth/strategies/local.strategy.ts
@@ -3,7 +3,7 @@ import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-local';
import { AuthService } from '../auth.service';
-import { AuthStrategy } from '../constants';
+import { AuthStrategy } from '../constant';
@Injectable()
export class LocalStrategy extends PassportStrategy(
diff --git a/apps/api/src/modules/health/health.controller.ts b/apps/api/src/modules/health/health.controller.ts
index a29a19b..2678295 100644
--- a/apps/api/src/modules/health/health.controller.ts
+++ b/apps/api/src/modules/health/health.controller.ts
@@ -8,7 +8,7 @@ import {
TypeOrmHealthIndicator,
} from '@nestjs/terminus';
-import { Permission } from '../rbac/decorators';
+import { Permission } from '../auth/decorators/permission.decorator';
export const PermissionHealth = {
NETWORK: 'app:health:network',
diff --git a/apps/api/src/modules/rbac/decorators/index.ts b/apps/api/src/modules/rbac/decorators/index.ts
deleted file mode 100644
index f4fae24..0000000
--- a/apps/api/src/modules/rbac/decorators/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './allow-anon.decorator';
-export * from './permission.decorator';
diff --git a/apps/api/src/modules/rbac/rbac.module.ts b/apps/api/src/modules/rbac/rbac.module.ts
deleted file mode 100644
index 683da98..0000000
--- a/apps/api/src/modules/rbac/rbac.module.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { Module, forwardRef } from '@nestjs/common';
-import { TypeOrmModule } from '@nestjs/typeorm';
-
-import { SystemModule } from '../system/system.module';
-
-const services = [];
-
-@Module({
- imports: [forwardRef(() => SystemModule)],
- controllers: [],
- providers: [...services],
- exports: [TypeOrmModule, ...services],
-})
-export class RbacModule {}
diff --git a/apps/api/src/modules/shared/services/app-logger.service.ts b/apps/api/src/modules/shared/services/app-logger.service.ts
deleted file mode 100644
index b8bcf23..0000000
--- a/apps/api/src/modules/shared/services/app-logger.service.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-import {
- ConsoleLogger,
- ConsoleLoggerOptions,
- Injectable,
- LogLevel,
-} from '@nestjs/common';
-
-import { ConfigService } from '@nestjs/config';
-import { Appender, configure, getLogger, levels } from 'log4js';
-
-/**
- * 将Nestjs内置日志等级进行等级排序, 并将内置log等级调整为info
- */
-const LogLevelOrder: LogLevel[] = ['verbose', 'debug', 'log', 'warn', 'error'];
-
-@Injectable()
-export class AppLoggerService extends ConsoleLogger {
- constructor(
- context: string,
- options: ConsoleLoggerOptions,
- private readonly configService: ConfigService,
- ) {
- // 设置日志等级
- const level = configService.get('app.logger.level');
- const levelIndex = LogLevelOrder.findIndex((e) => e === level);
- if (levelIndex === -1) {
- throw new Error(
- `Invalid logger level, configurable level ${LogLevelOrder.join(',')}`,
- );
- }
-
- super(context, {
- ...options,
- timestamp: true,
- logLevels: LogLevelOrder.slice(levelIndex),
- });
-
- // 初始化log4js
- this.initLog4js();
- }
-
- verbose(message: any, context?: string): void {
- super.verbose.apply(this, [message, context]);
-
- if (this.isLevelEnabled('verbose')) {
- getLogger('verbose').log('verbose', message);
- }
- }
-
- debug(message: any, context?: string): void {
- super.debug.apply(this, [message, context]);
-
- if (this.isLevelEnabled('debug')) {
- getLogger('debug').log('debug', message);
- }
- }
-
- log(message: any, context?: string): void {
- super.log.apply(this, [message, context]);
-
- if (this.isLevelEnabled('log')) {
- getLogger('info').log('info', message);
- }
- }
-
- warn(message: any, context?: string): void {
- super.warn.apply(this, [message, context]);
-
- if (this.isLevelEnabled('warn')) {
- getLogger('warn').log('warn', message);
- }
- }
-
- error(message: any, stack?: string, context?: string): void {
- super.error.apply(this, [message, stack, context]);
-
- if (this.isLevelEnabled('error')) {
- getLogger('error').log('error', message);
- }
- }
-
- private initLog4js() {
- // 增加日志等级
- levels.addLevels({
- VERBOSE: { value: 5000, colour: 'blue' },
- DEBUG: { value: 10000, colour: 'cyan' },
- INFO: { value: 20000, colour: 'green' },
- WARN: { value: 30000, colour: 'yellow' },
- ERROR: { value: 40000, colour: 'red' },
- });
-
- configure({
- appenders: {
- verbose: this.createAppenders('verbose'),
- debug: this.createAppenders('debug'),
- info: this.createAppenders('info'),
- warn: this.createAppenders('warn'),
- error: this.createAppenders('error'),
- console: {
- type: 'console',
- },
- },
- categories: {
- default: {
- appenders: ['console'],
- level: 'all',
- },
- verbose: {
- appenders: ['verbose'],
- level: 'all',
- },
- debug: {
- appenders: ['debug'],
- level: 'all',
- },
- info: {
- appenders: ['info'],
- level: 'all',
- },
- warn: {
- appenders: ['warn'],
- level: 'all',
- },
- error: {
- appenders: ['error'],
- level: 'all',
- enableCallStack: true,
- },
- },
- });
- }
-
- private createAppenders(level: LogLevel | 'info'): Appender {
- const enableCallStack = level === 'error';
-
- return {
- type: 'dateFile',
- filename: `logs/${level}`,
- pattern: 'yyyy-MM-dd.log',
- alwaysIncludePattern: true,
- keepFileExt: true,
- numBackups: this.configService.get('app.logger.maxFiles'),
- layout: {
- type: 'pattern',
- pattern:
- '[%h] %z %d{yyyy-MM-dd hh:mm:ss.SSS} %p %n%m' +
- `${enableCallStack ? ' %n%s' : ''}` +
- ' %n%x{divider}',
- tokens: {
- divider: '-'.repeat(150),
- },
- },
- };
- }
-}
diff --git a/apps/api/src/modules/socket/admin-ws.guard.ts b/apps/api/src/modules/socket/admin-ws.guard.ts
index db8d76b..90d383d 100644
--- a/apps/api/src/modules/socket/admin-ws.guard.ts
+++ b/apps/api/src/modules/socket/admin-ws.guard.ts
@@ -2,8 +2,8 @@ import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Socket } from 'socket.io';
+import { SocketException } from '@/common/exceptions/socket.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { SocketException } from '@/exceptions/socket.exception';
import { AuthService } from './auth.service';
diff --git a/apps/api/src/modules/socket/admin-ws.service.ts b/apps/api/src/modules/socket/admin-ws.service.ts
index ebe0947..cfbc77e 100644
--- a/apps/api/src/modules/socket/admin-ws.service.ts
+++ b/apps/api/src/modules/socket/admin-ws.service.ts
@@ -7,7 +7,7 @@ import { In, Repository } from 'typeorm';
import { AdminWSGateway } from '@/modules/socket/admin-ws.gateway';
import { RoleEntity } from '../system/role/role.entity';
-import { UserEntity } from '../system/user/entities/user.entity';
+import { UserEntity } from '../user/entities/user.entity';
import { EVENT_UPDATE_MENU } from './socket.event';
diff --git a/apps/api/src/modules/socket/auth.service.ts b/apps/api/src/modules/socket/auth.service.ts
index ed464ef..75290e9 100644
--- a/apps/api/src/modules/socket/auth.service.ts
+++ b/apps/api/src/modules/socket/auth.service.ts
@@ -2,10 +2,9 @@ import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { isEmpty } from 'lodash';
+import { SocketException } from '@/common/exceptions/socket.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { SocketException } from '@/exceptions/socket.exception';
-
@Injectable()
export class AuthService {
constructor(private jwtService: JwtService) {}
diff --git a/apps/api/src/modules/system/dept/dept.controller.ts b/apps/api/src/modules/system/dept/dept.controller.ts
index 7ca2fe8..c148866 100644
--- a/apps/api/src/modules/system/dept/dept.controller.ts
+++ b/apps/api/src/modules/system/dept/dept.controller.ts
@@ -9,18 +9,25 @@ import {
} from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
+import { IdParam } from '@/common/decorators/id-param.decorator';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiResult } from '@/decorators/api-result.decorator';
-import { IdParam } from '@/decorators/id-param.decorator';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
-import { ApiException } from '@/exceptions/api.exception';
-import { AuthUser } from '@/modules/auth/decorators';
-import { Permission } from '@/modules/rbac/decorators';
+import { AuthUser } from '@/modules/auth/decorators/auth-user.decorator';
+import { Permission } from '@/modules/auth/decorators/permission.decorator';
import { DeptEntity } from '@/modules/system/dept/dept.entity';
import { DeptDto, DeptQueryDto } from './dept.dto';
import { DeptService } from './dept.service';
-import { PermissionDept } from './permission';
+
+export const Permissions = {
+ LIST: 'system:dept:list',
+ CREATE: 'system:dept:create',
+ READ: 'system:dept:read',
+ UPDATE: 'system:dept:update',
+ DELETE: 'system:dept:delete',
+} as const;
@ApiSecurityAuth()
@ApiTags('System - 部门模块')
@@ -31,7 +38,7 @@ export class DeptController {
@Get()
@ApiOperation({ summary: '获取部门列表' })
@ApiResult({ type: [DeptEntity] })
- @Permission(PermissionDept.LIST)
+ @Permission(Permissions.LIST)
async list(
@Query() dto: DeptQueryDto,
@AuthUser('uid') uid: number,
@@ -41,21 +48,21 @@ export class DeptController {
@Post()
@ApiOperation({ summary: '创建部门' })
- @Permission(PermissionDept.CREATE)
+ @Permission(Permissions.CREATE)
async create(@Body() dto: DeptDto): Promise {
await this.deptService.create(dto);
}
@Get(':id')
@ApiOperation({ summary: '查询部门信息' })
- @Permission(PermissionDept.READ)
+ @Permission(Permissions.READ)
async info(@IdParam() id: number) {
return this.deptService.info(id);
}
@Put(':id')
@ApiOperation({ summary: '更新部门' })
- @Permission(PermissionDept.UPDATE)
+ @Permission(Permissions.UPDATE)
async update(
@IdParam() id: number,
@Body() updateDeptDto: DeptDto,
@@ -65,17 +72,17 @@ export class DeptController {
@Delete(':id')
@ApiOperation({ summary: '删除部门' })
- @Permission(PermissionDept.DELETE)
+ @Permission(Permissions.DELETE)
async delete(@IdParam() id: number): Promise {
// 查询是否有关联用户或者部门,如果含有则无法删除
const count = await this.deptService.countUserByDeptId(id);
if (count > 0)
- throw new ApiException(ErrorEnum.DEPARTMENT_HAS_ASSOCIATED_USERS);
+ throw new BusinessException(ErrorEnum.DEPARTMENT_HAS_ASSOCIATED_USERS);
const count2 = await this.deptService.countChildDept(id);
if (count2 > 0)
- throw new ApiException(ErrorEnum.DEPARTMENT_HAS_CHILD_DEPARTMENTS);
+ throw new BusinessException(ErrorEnum.DEPARTMENT_HAS_CHILD_DEPARTMENTS);
await this.deptService.delete(id);
}
diff --git a/apps/api/src/modules/system/dept/dept.entity.ts b/apps/api/src/modules/system/dept/dept.entity.ts
index 11406c2..a47913d 100644
--- a/apps/api/src/modules/system/dept/dept.entity.ts
+++ b/apps/api/src/modules/system/dept/dept.entity.ts
@@ -10,7 +10,7 @@ import {
import { AbstractEntity } from '@/common/entity/abstract.entity';
-import { UserEntity } from '../user/entities/user.entity';
+import { UserEntity } from '../../user/entities/user.entity';
@Entity({ name: 'sys_dept' })
@Tree('materialized-path')
diff --git a/apps/api/src/modules/system/dept/dept.module.ts b/apps/api/src/modules/system/dept/dept.module.ts
index 9d03567..aeade9c 100644
--- a/apps/api/src/modules/system/dept/dept.module.ts
+++ b/apps/api/src/modules/system/dept/dept.module.ts
@@ -1,8 +1,8 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
+import { UserModule } from '../../user/user.module';
import { RoleModule } from '../role/role.module';
-import { UserModule } from '../user/user.module';
import { DeptController } from './dept.controller';
import { DeptEntity } from './dept.entity';
diff --git a/apps/api/src/modules/system/dept/dept.service.ts b/apps/api/src/modules/system/dept/dept.service.ts
index 5c47354..ff5d7a5 100644
--- a/apps/api/src/modules/system/dept/dept.service.ts
+++ b/apps/api/src/modules/system/dept/dept.service.ts
@@ -3,10 +3,10 @@ import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
import { isEmpty } from 'lodash';
import { EntityManager, Repository, TreeRepository } from 'typeorm';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiException } from '@/exceptions/api.exception';
import { DeptEntity } from '@/modules/system/dept/dept.entity';
-import { UserEntity } from '@/modules/system/user/entities/user.entity';
+import { UserEntity } from '@/modules/user/entities/user.entity';
import { deleteEmptyChildren } from '@/utils/list2tree';
@@ -37,7 +37,7 @@ export class DeptService {
.getOne();
if (isEmpty(dept)) {
- throw new ApiException(ErrorEnum.DEPARTMENT_NOT_FOUND);
+ throw new BusinessException(ErrorEnum.DEPARTMENT_NOT_FOUND);
}
return dept;
}
diff --git a/apps/api/src/modules/system/dept/permission.ts b/apps/api/src/modules/system/dept/permission.ts
deleted file mode 100644
index 59539a4..0000000
--- a/apps/api/src/modules/system/dept/permission.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export const PermissionDept = {
- LIST: 'system:dept:list',
- CREATE: 'system:dept:create',
- READ: 'system:dept:read',
- UPDATE: 'system:dept:update',
- DELETE: 'system:dept:delete',
-} as const;
diff --git a/apps/api/src/modules/system/dict/dict.controller.ts b/apps/api/src/modules/system/dict/dict.controller.ts
index e01c52c..d4b6d5f 100644
--- a/apps/api/src/modules/system/dict/dict.controller.ts
+++ b/apps/api/src/modules/system/dict/dict.controller.ts
@@ -1,16 +1,23 @@
import { Body, Controller, Delete, Get, Post, Query } from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
-import { ApiResult } from '@/decorators/api-result.decorator';
-import { IdParam } from '@/decorators/id-param.decorator';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
+import { IdParam } from '@/common/decorators/id-param.decorator';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
import { Pagination } from '@/helper/paginate/pagination';
-import { Permission } from '@/modules/rbac/decorators';
+import { Permission } from '@/modules/auth/decorators/permission.decorator';
import { DictEntity } from '@/modules/system/dict/dict.entity';
import { DictDto, DictQueryDto } from './dict.dto';
import { DictService } from './dict.service';
-import { PermissionDict } from './permission';
+
+export const Permissions = {
+ LIST: 'system:dict:list',
+ CREATE: 'system:dict:create',
+ READ: 'system:dict:read',
+ UPDATE: 'system:dict:update',
+ DELETE: 'system:dict:delete',
+} as const;
@ApiTags('System - 字典配置模块')
@ApiSecurityAuth()
@@ -21,14 +28,14 @@ export class DictController {
@Get()
@ApiOperation({ summary: '获取字典配置列表' })
@ApiResult({ type: [DictEntity] })
- @Permission(PermissionDict.LIST)
+ @Permission(Permissions.LIST)
async list(@Query() dto: DictQueryDto): Promise> {
return this.dictService.page(dto);
}
@Post()
@ApiOperation({ summary: '新增字典配置' })
- @Permission(PermissionDict.CREATE)
+ @Permission(Permissions.CREATE)
async create(@Body() dto: DictDto): Promise {
await this.dictService.isExistKey(dto.key);
await this.dictService.create(dto);
@@ -37,21 +44,21 @@ export class DictController {
@Get(':id')
@ApiOperation({ summary: '查询字典配置信息' })
@ApiResult({ type: DictEntity })
- @Permission(PermissionDict.READ)
+ @Permission(Permissions.READ)
async info(@IdParam() id: number): Promise {
return this.dictService.findOne(id);
}
@Post(':id')
@ApiOperation({ summary: '更新字典配置' })
- @Permission(PermissionDict.UPDATE)
+ @Permission(Permissions.UPDATE)
async update(@IdParam() id: number, @Body() dto: DictDto): Promise {
await this.dictService.update(id, dto);
}
@Delete(':id')
@ApiOperation({ summary: '删除指定的字典配置' })
- @Permission(PermissionDict.DELETE)
+ @Permission(Permissions.DELETE)
async delete(@IdParam() id: number): Promise {
await this.dictService.delete(id);
}
diff --git a/apps/api/src/modules/system/dict/dict.service.ts b/apps/api/src/modules/system/dict/dict.service.ts
index 15e4d5c..a8abbf7 100644
--- a/apps/api/src/modules/system/dict/dict.service.ts
+++ b/apps/api/src/modules/system/dict/dict.service.ts
@@ -3,8 +3,8 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiException } from '@/exceptions/api.exception';
import { paginate } from '@/helper/paginate';
import { Pagination } from '@/helper/paginate/pagination';
import { DictEntity } from '@/modules/system/dict/dict.entity';
@@ -75,7 +75,7 @@ export class DictService {
async isExistKey(key: string): Promise {
const result = await this.dictRepository.findOneBy({ key });
if (result) {
- throw new ApiException(ErrorEnum.PARAMETER_CONFIG_KEY_EXISTS);
+ throw new BusinessException(ErrorEnum.PARAMETER_CONFIG_KEY_EXISTS);
}
}
diff --git a/apps/api/src/modules/system/dict/permission.ts b/apps/api/src/modules/system/dict/permission.ts
deleted file mode 100644
index 238f6e0..0000000
--- a/apps/api/src/modules/system/dict/permission.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export const PermissionDict = {
- LIST: 'system:dict:list',
- CREATE: 'system:dict:create',
- READ: 'system:dict:read',
- UPDATE: 'system:dict:update',
- DELETE: 'system:dict:delete',
-} as const;
diff --git a/apps/api/src/modules/system/log/entities/login-log.entity.ts b/apps/api/src/modules/system/log/entities/login-log.entity.ts
index bd8d3ce..3c7955d 100644
--- a/apps/api/src/modules/system/log/entities/login-log.entity.ts
+++ b/apps/api/src/modules/system/log/entities/login-log.entity.ts
@@ -3,7 +3,7 @@ import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm';
import { AbstractEntity } from '@/common/entity/abstract.entity';
-import { UserEntity } from '../../user/entities/user.entity';
+import { UserEntity } from '../../../user/entities/user.entity';
@Entity({ name: 'sys_login_log' })
export class LoginLogEntity extends AbstractEntity {
diff --git a/apps/api/src/modules/system/log/log.controller.ts b/apps/api/src/modules/system/log/log.controller.ts
index fbfa4a8..34a0c20 100644
--- a/apps/api/src/modules/system/log/log.controller.ts
+++ b/apps/api/src/modules/system/log/log.controller.ts
@@ -1,11 +1,11 @@
import { Controller, Get, Query } from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
-import { ApiResult } from '@/decorators';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
import { Pagination } from '@/helper/paginate/pagination';
-import { Permission } from '@/modules/rbac/decorators';
+import { Permission } from '@/modules/auth/decorators/permission.decorator';
import {
CaptchaLogQueryDto,
@@ -14,11 +14,17 @@ import {
} from './dto/log.dto';
import { CaptchaLogEntity } from './entities/captcha-log.entity';
import { TaskLogEntity } from './entities/task-log.entity';
-import { LoginLogInfo } from './log.modal';
+import { LoginLogInfo } from './models/log.model';
import { CaptchaLogService } from './services/captcha-log.service';
import { LoginLogService } from './services/login-log.service';
import { TaskLogService } from './services/task-log.service';
+export const Permissions = {
+ TaskList: 'system:log:task:list',
+ LogList: 'system:log:login:list',
+ CaptchaList: 'system:log:captcha:list',
+};
+
@ApiSecurityAuth()
@ApiTags('System - 日志模块')
@Controller('log')
@@ -32,7 +38,7 @@ export class LogController {
@Get('login/list')
@ApiOperation({ summary: '查询登录日志列表' })
@ApiResult({ type: [LoginLogInfo], isPage: true })
- @Permission('system:log:task:list')
+ @Permission(Permissions.TaskList)
async loginLogPage(
@Query() dto: LoginLogQueryDto,
): Promise> {
@@ -42,7 +48,7 @@ export class LogController {
@Get('task/list')
@ApiOperation({ summary: '查询任务日志列表' })
@ApiResult({ type: [TaskLogEntity], isPage: true })
- @Permission('system:log:task:list')
+ @Permission(Permissions.LogList)
async taskList(@Query() dto: TaskLogQueryDto) {
return this.taskService.list(dto);
}
@@ -50,7 +56,7 @@ export class LogController {
@Get('captcha/list')
@ApiOperation({ summary: '查询验证码日志列表' })
@ApiResult({ type: [CaptchaLogEntity], isPage: true })
- @Permission('system:log:captcha:list')
+ @Permission(Permissions.CaptchaList)
async captchaList(
@Query() dto: CaptchaLogQueryDto,
): Promise> {
diff --git a/apps/api/src/modules/system/log/log.modal.ts b/apps/api/src/modules/system/log/log.modal.ts
deleted file mode 100644
index a962a2f..0000000
--- a/apps/api/src/modules/system/log/log.modal.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { ApiProperty } from '@nestjs/swagger';
-
-export class LoginLogInfo {
- @ApiProperty({ description: '日志编号' })
- id: number;
-
- @ApiProperty({ description: '登录ip', example: '1.1.1.1' })
- ip: string;
-
- @ApiProperty({ description: '登录地址' })
- address: string;
-
- @ApiProperty({ description: '系统', example: 'Windows 10' })
- os: string;
-
- @ApiProperty({ description: '浏览器', example: 'Chrome' })
- browser: string;
-
- @ApiProperty({ description: '时间', example: '2022-01-01 00:00:00' })
- time: string;
-
- @ApiProperty({ description: '登录用户名', example: 'admin' })
- username: string;
-}
diff --git a/apps/api/src/modules/system/log/log.module.ts b/apps/api/src/modules/system/log/log.module.ts
index 42782e0..7f1b5b8 100644
--- a/apps/api/src/modules/system/log/log.module.ts
+++ b/apps/api/src/modules/system/log/log.module.ts
@@ -1,7 +1,7 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
-import { UserModule } from '../user/user.module';
+import { UserModule } from '../../user/user.module';
import { CaptchaLogEntity } from './entities/captcha-log.entity';
import { LoginLogEntity } from './entities/login-log.entity';
diff --git a/apps/api/src/modules/system/log/services/login-log.service.ts b/apps/api/src/modules/system/log/services/login-log.service.ts
index 450a171..2338cc5 100644
--- a/apps/api/src/modules/system/log/services/login-log.service.ts
+++ b/apps/api/src/modules/system/log/services/login-log.service.ts
@@ -7,11 +7,11 @@ import UAParser from 'ua-parser-js';
import { paginateRaw } from '@/helper/paginate';
-import { IpService } from '@/modules/shared/ip/ip.service';
+import { IpService } from '@/shared/ip/ip.service';
import { LoginLogQueryDto } from '../dto/log.dto';
import { LoginLogEntity } from '../entities/login-log.entity';
-import { LoginLogInfo } from '../log.modal';
+import { LoginLogInfo } from '../models/log.model';
async function parseLoginLog(e: any, parser: UAParser): Promise {
const uaResult = parser.setUA(e.login_log_ua).getResult();
diff --git a/apps/api/src/modules/system/menu/menu.controller.ts b/apps/api/src/modules/system/menu/menu.controller.ts
index ca1f789..67c9504 100644
--- a/apps/api/src/modules/system/menu/menu.controller.ts
+++ b/apps/api/src/modules/system/menu/menu.controller.ts
@@ -1,4 +1,5 @@
import {
+ BadRequestException,
Body,
Controller,
Delete,
@@ -7,50 +8,50 @@ import {
Put,
Query,
} from '@nestjs/common';
-import { ConfigService } from '@nestjs/config';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { flattenDeep } from 'lodash';
-import { IAppConfig } from '@/config';
-import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiResult } from '@/decorators/api-result.decorator';
-import { IdParam } from '@/decorators/id-param.decorator';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
-import { ApiException } from '@/exceptions/api.exception';
-import { Permission } from '@/modules/rbac/decorators';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
+import { IdParam } from '@/common/decorators/id-param.decorator';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
+import { Permission } from '@/modules/auth/decorators/permission.decorator';
import { MenuEntity } from '@/modules/system/menu/menu.entity';
import { MenuDto, MenuQueryDto } from './menu.dto';
import { MenuService } from './menu.service';
-import { PermissionMenu } from './permission';
+
+export const Permissions = {
+ LIST: 'system:menu:list',
+ CREATE: 'system:menu:create',
+ READ: 'system:menu:read',
+ UPDATE: 'system:menu:update',
+ DELETE: 'system:menu:delete',
+} as const;
@ApiTags('System - 菜单权限模块')
@ApiSecurityAuth()
@Controller('menus')
export class MenuController {
- constructor(
- private menuService: MenuService,
- private configService: ConfigService,
- ) {}
+ constructor(private menuService: MenuService) {}
@Get()
@ApiOperation({ summary: '获取所有菜单列表' })
@ApiResult({ type: [MenuEntity] })
- @Permission(PermissionMenu.LIST)
+ @Permission(Permissions.LIST)
async list(@Query() dto: MenuQueryDto) {
return this.menuService.list(dto);
}
@Get(':id')
@ApiOperation({ summary: '获取菜单或权限信息' })
- @Permission(PermissionMenu.READ)
+ @Permission(Permissions.READ)
async info(@IdParam() id: number) {
return this.menuService.getMenuItemAndParentInfo(id);
}
@Post()
@ApiOperation({ summary: '新增菜单或权限' })
- @Permission(PermissionMenu.CREATE)
+ @Permission(Permissions.CREATE)
async create(@Body() dto: MenuDto): Promise {
// check
await this.menuService.check(dto);
@@ -70,14 +71,11 @@ export class MenuController {
@Put(':id')
@ApiOperation({ summary: '更新菜单或权限' })
- @Permission(PermissionMenu.UPDATE)
+ @Permission(Permissions.UPDATE)
async update(
@IdParam() id: number,
@Body() dto: Partial,
): Promise {
- if (id <= this.configService.get('app').protectSysPermMenuMaxId)
- throw new ApiException(ErrorEnum.SYSTEM_BUILTIN_FUNCTION_NOT_ALLOWED);
-
// check
await this.menuService.check(dto);
if (dto.parent === -1 || !dto.parent) {
@@ -93,15 +91,12 @@ export class MenuController {
@Delete(':id')
@ApiOperation({ summary: '删除菜单或权限' })
- @Permission(PermissionMenu.DELETE)
+ @Permission(Permissions.DELETE)
async delete(@IdParam() id: number): Promise {
- // 68为内置init.sql中插入最后的索引编号
- if (
- id <= this.configService.get('app').protectSysPermMenuMaxId
- ) {
- // 系统内置功能不提供删除
- throw new ApiException(ErrorEnum.SYSTEM_BUILTIN_FUNCTION_NOT_ALLOWED);
+ if (await this.menuService.checkRoleByMenuId(id)) {
+ throw new BadRequestException('该菜单存在关联角色,无法删除');
}
+
// 如果有子目录,一并删除
const childMenus = await this.menuService.findChildMenus(id);
await this.menuService.deleteMenuItem(flattenDeep([id, childMenus]));
diff --git a/apps/api/src/modules/system/menu/menu.service.ts b/apps/api/src/modules/system/menu/menu.service.ts
index fe0a943..ae5765e 100644
--- a/apps/api/src/modules/system/menu/menu.service.ts
+++ b/apps/api/src/modules/system/menu/menu.service.ts
@@ -6,8 +6,8 @@ import { concat, isEmpty, uniq } from 'lodash';
import { In, IsNull, Like, Not, Repository } from 'typeorm';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiException } from '@/exceptions/api.exception';
import { MenuEntity } from '@/modules/system/menu/menu.entity';
import { deleteEmptyChildren } from '@/utils';
@@ -92,16 +92,18 @@ export class MenuService {
async check(dto: Partial): Promise {
if (dto.type === 2 && !dto.parent) {
// 无法直接创建权限,必须有parent
- throw new ApiException(ErrorEnum.PERMISSION_REQUIRES_PARENT);
+ throw new BusinessException(ErrorEnum.PERMISSION_REQUIRES_PARENT);
}
if (dto.type === 1 && dto.parent) {
const parent = await this.getMenuItemInfo(dto.parent);
if (isEmpty(parent)) {
- throw new ApiException(ErrorEnum.PARENT_MENU_NOT_FOUND);
+ throw new BusinessException(ErrorEnum.PARENT_MENU_NOT_FOUND);
}
if (parent && parent.type === 1) {
// 当前新增为菜单但父节点也为菜单时为非法操作
- throw new ApiException(ErrorEnum.ILLEGAL_OPERATION_DIRECTORY_PARENT);
+ throw new BusinessException(
+ ErrorEnum.ILLEGAL_OPERATION_DIRECTORY_PARENT,
+ );
}
}
}
@@ -225,4 +227,17 @@ export class MenuService {
});
}
}
+
+ /**
+ * 根据菜单ID查找是否有关联角色
+ */
+ async checkRoleByMenuId(id: number): Promise {
+ return !!(await this.menuRepository.findOne({
+ where: {
+ roles: {
+ id,
+ },
+ },
+ }));
+ }
}
diff --git a/apps/api/src/modules/system/menu/permission.ts b/apps/api/src/modules/system/menu/permission.ts
deleted file mode 100644
index 7df4e62..0000000
--- a/apps/api/src/modules/system/menu/permission.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export const PermissionMenu = {
- LIST: 'system:menu:list',
- CREATE: 'system:menu:create',
- READ: 'system:menu:read',
- UPDATE: 'system:menu:update',
- DELETE: 'system:menu:delete',
-} as const;
diff --git a/apps/api/src/modules/system/online/online.controller.ts b/apps/api/src/modules/system/online/online.controller.ts
index d9b46d7..a248a29 100644
--- a/apps/api/src/modules/system/online/online.controller.ts
+++ b/apps/api/src/modules/system/online/online.controller.ts
@@ -1,14 +1,14 @@
import { Body, Controller, Get, Post } from '@nestjs/common';
import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiResult } from '@/decorators/api-result.decorator';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
-import { ApiException } from '@/exceptions/api.exception';
-import { AuthUser } from '@/modules/auth/decorators';
+import { AuthUser } from '@/modules/auth/decorators/auth-user.decorator';
-import { Permission } from '@/modules/rbac/decorators';
+import { Permission } from '@/modules/auth/decorators/permission.decorator';
import { KickDto } from './online.dto';
import { OnlineUserInfo } from './online.model';
@@ -34,7 +34,7 @@ export class OnlineController {
@Permission('system:online:kick')
async kick(@Body() dto: KickDto, @AuthUser() user: IAuthUser): Promise {
if (dto.id === user.uid) {
- throw new ApiException(ErrorEnum.NOT_ALLOWED_TO_LOGOUT_USER);
+ throw new BusinessException(ErrorEnum.NOT_ALLOWED_TO_LOGOUT_USER);
}
await this.onlineService.kickUser(dto.id, user.uid);
}
diff --git a/apps/api/src/modules/system/online/online.module.ts b/apps/api/src/modules/system/online/online.module.ts
index d68ce63..e11fe6e 100644
--- a/apps/api/src/modules/system/online/online.module.ts
+++ b/apps/api/src/modules/system/online/online.module.ts
@@ -3,11 +3,10 @@ import { Module, forwardRef } from '@nestjs/common';
import { AuthModule } from '@/modules/auth/auth.module';
import { SocketModule } from '@/modules/socket/socket.module';
+import { UserModule } from '../../user/user.module';
import { RoleModule } from '../role/role.module';
import { SystemModule } from '../system.module';
-import { UserModule } from '../user/user.module';
-
import { OnlineController } from './online.controller';
import { OnlineService } from './online.service';
diff --git a/apps/api/src/modules/system/online/online.service.ts b/apps/api/src/modules/system/online/online.service.ts
index da8a736..928c0a9 100644
--- a/apps/api/src/modules/system/online/online.service.ts
+++ b/apps/api/src/modules/system/online/online.service.ts
@@ -6,13 +6,13 @@ import { EntityManager } from 'typeorm';
import { UAParser } from 'ua-parser-js';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiException } from '@/exceptions/api.exception';
import { AdminWSGateway } from '@/modules/socket/admin-ws.gateway';
import { AdminWSService } from '@/modules/socket/admin-ws.service';
import { EVENT_KICK } from '@/modules/socket/socket.event';
-import { UserService } from '../user/user.service';
+import { UserService } from '../../user/user.service';
import { OnlineUserInfo } from './online.model';
@@ -48,7 +48,7 @@ export class OnlineService {
const rootUserId = await this.userService.findRootUserId();
const currentUserInfo = await this.userService.getAccountInfo(currentUid);
if (uid === rootUserId) {
- throw new ApiException(ErrorEnum.NOT_ALLOWED_TO_LOGOUT_USER);
+ throw new BusinessException(ErrorEnum.NOT_ALLOWED_TO_LOGOUT_USER);
}
// reset redis keys
await this.userService.forbidden(uid);
diff --git a/apps/api/src/modules/system/role/permission.ts b/apps/api/src/modules/system/role/permission.ts
deleted file mode 100644
index 02a4963..0000000
--- a/apps/api/src/modules/system/role/permission.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export const PermissionRole = {
- LIST: 'system:role:list',
- CREATE: 'system:role:create',
- READ: 'system:role:read',
- UPDATE: 'system:role:update',
- DELETE: 'system:role:delete',
-} as const;
diff --git a/apps/api/src/modules/system/role/role.controller.ts b/apps/api/src/modules/system/role/role.controller.ts
index 426c5a0..36e048f 100644
--- a/apps/api/src/modules/system/role/role.controller.ts
+++ b/apps/api/src/modules/system/role/role.controller.ts
@@ -1,4 +1,5 @@
import {
+ BadRequestException,
Body,
Controller,
Delete,
@@ -9,19 +10,26 @@ import {
} from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
+import { IdParam } from '@/common/decorators/id-param.decorator';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
import { PageOptionsDto } from '@/common/dto/page-options.dto';
-import { ApiResult } from '@/decorators/api-result.decorator';
-import { IdParam } from '@/decorators/id-param.decorator';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
-import { Permission } from '@/modules/rbac/decorators';
+import { Permission } from '@/modules/auth/decorators/permission.decorator';
import { RoleEntity } from '@/modules/system/role/role.entity';
import { MenuService } from '../menu/menu.service';
-import { PermissionRole } from './permission';
import { RoleDto } from './role.dto';
import { RoleService } from './role.service';
+export const Permissions = {
+ LIST: 'system:role:list',
+ CREATE: 'system:role:create',
+ READ: 'system:role:read',
+ UPDATE: 'system:role:update',
+ DELETE: 'system:role:delete',
+} as const;
+
@ApiTags('System - 角色模块')
@ApiSecurityAuth()
@Controller('roles')
@@ -34,7 +42,7 @@ export class RoleController {
@Get()
@ApiOperation({ summary: '获取角色列表' })
@ApiResult({ type: [RoleEntity] })
- @Permission(PermissionRole.LIST)
+ @Permission(Permissions.LIST)
async list(@Query() dto: PageOptionsDto) {
return this.roleService.findAll(dto);
}
@@ -42,21 +50,21 @@ export class RoleController {
@Get(':id')
@ApiOperation({ summary: '获取角色信息' })
@ApiResult({ type: RoleEntity })
- @Permission(PermissionRole.READ)
+ @Permission(Permissions.READ)
async info(@IdParam() id: number) {
return this.roleService.info(id);
}
@Post()
@ApiOperation({ summary: '新增角色' })
- @Permission(PermissionRole.CREATE)
+ @Permission(Permissions.CREATE)
async create(@Body() dto: RoleDto): Promise {
await this.roleService.create(dto);
}
@Put(':id')
@ApiOperation({ summary: '更新角色' })
- @Permission(PermissionRole.UPDATE)
+ @Permission(Permissions.UPDATE)
async update(
@IdParam() id: number,
@Body() dto: Partial,
@@ -67,8 +75,12 @@ export class RoleController {
@Delete(':id')
@ApiOperation({ summary: '删除角色' })
- @Permission(PermissionRole.DELETE)
+ @Permission(Permissions.DELETE)
async delete(@IdParam() id: number): Promise {
+ if (await this.roleService.checkUserByRoleId(id)) {
+ throw new BadRequestException('该角色存在关联用户,无法删除');
+ }
+
await this.roleService.delete(id);
await this.menuService.refreshOnlineUserPerms();
}
diff --git a/apps/api/src/modules/system/role/role.entity.ts b/apps/api/src/modules/system/role/role.entity.ts
index d6ed580..a5941e8 100644
--- a/apps/api/src/modules/system/role/role.entity.ts
+++ b/apps/api/src/modules/system/role/role.entity.ts
@@ -9,8 +9,8 @@ import {
import { AbstractEntity } from '@/common/entity/abstract.entity';
+import { UserEntity } from '../../user/entities/user.entity';
import { MenuEntity } from '../menu/menu.entity';
-import { UserEntity } from '../user/entities/user.entity';
@Entity({ name: 'sys_role' })
export class RoleEntity extends AbstractEntity {
diff --git a/apps/api/src/modules/system/role/role.service.ts b/apps/api/src/modules/system/role/role.service.ts
index c9c0918..0f197cb 100644
--- a/apps/api/src/modules/system/role/role.service.ts
+++ b/apps/api/src/modules/system/role/role.service.ts
@@ -1,7 +1,7 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
-import { difference, isEmpty } from 'lodash';
+import { isEmpty } from 'lodash';
import { EntityManager, In, Repository } from 'typeorm';
import { PageOptionsDto } from '@/common/dto/page-options.dto';
@@ -38,21 +38,21 @@ export class RoleService {
/**
* 根据角色获取角色信息
*/
- async info(rid: number): Promise {
+ async info(id: number): Promise {
const info = await this.roleRepository
.createQueryBuilder('role')
.where({
- id: rid,
+ id,
})
.getOne();
- if (rid === this.configService.get('app').adminRoleId) {
- const menus = await this.menuRepository.find({ select: ['id'] });
- return { ...info, menuIds: menus.map((m) => m.id) };
- }
+ // if (id === this.configService.get('app').adminRoleId) {
+ // const menus = await this.menuRepository.find({ select: ['id'] });
+ // return { ...info, menuIds: menus.map((m) => m.id) };
+ // }
const menus = await this.menuRepository.find({
- where: { roles: { id: rid } },
+ where: { roles: { id } },
select: ['id'],
});
@@ -87,24 +87,18 @@ export class RoleService {
async update(id, { menuIds, ...data }: Partial): Promise {
await this.roleRepository.update(id, data);
- // 对比 menu 差异
- const originMenus = await this.menuRepository.find({
- where: { roles: { id } },
- });
- const originMenuIds = originMenus.map((m) => m.id);
- const insertMenusRowIds = difference(menuIds, originMenuIds);
- const deleteMenusRowIds = difference(originMenuIds, menuIds);
-
- // using transaction
- await this.entityManager.transaction(async (manager) => {
- if (!isEmpty(menuIds)) {
- await manager
- .createQueryBuilder()
- .relation(RoleEntity, 'menus')
- .of(id)
- .addAndRemove(insertMenusRowIds, deleteMenusRowIds);
- }
- });
+ if (!isEmpty(menuIds)) {
+ // using transaction
+ await this.entityManager.transaction(async (manager) => {
+ const menus = await this.menuRepository.find({
+ where: { id: In(menuIds) },
+ });
+
+ const role = await this.roleRepository.findOne({ where: { id } });
+ role.menus = menus;
+ await manager.save(role);
+ });
+ }
}
/**
@@ -151,4 +145,17 @@ export class RoleService {
(r) => r === this.configService.get('app').adminRoleId,
);
}
+
+ /**
+ * 根据角色ID查找是否有关联用户
+ */
+ async checkUserByRoleId(id: number): Promise {
+ return !!(await this.roleRepository.findOne({
+ where: {
+ users: {
+ id,
+ },
+ },
+ }));
+ }
}
diff --git a/apps/api/src/modules/system/serve/serve.controller.ts b/apps/api/src/modules/system/serve/serve.controller.ts
index 589842a..9c92f8c 100644
--- a/apps/api/src/modules/system/serve/serve.controller.ts
+++ b/apps/api/src/modules/system/serve/serve.controller.ts
@@ -2,11 +2,11 @@ import { CacheInterceptor, CacheKey, CacheTTL } from '@nestjs/cache-manager';
import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { ApiExtraModels, ApiOperation, ApiTags } from '@nestjs/swagger';
-import { ApiResult } from '@/decorators/api-result.decorator';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
-import { AllowAnon } from '@/modules/rbac/decorators';
+import { AllowAnon } from '@/modules/auth/decorators/allow-anon.decorator';
import { ServeStatInfo } from './serve.model';
import { ServeService } from './serve.service';
diff --git a/apps/api/src/modules/system/system.module.ts b/apps/api/src/modules/system/system.module.ts
index 6614f20..e573cdf 100644
--- a/apps/api/src/modules/system/system.module.ts
+++ b/apps/api/src/modules/system/system.module.ts
@@ -2,6 +2,8 @@ import { Module } from '@nestjs/common';
import { RouterModule } from '@nestjs/core';
+import { UserModule } from '../user/user.module';
+
import { DeptModule } from './dept/dept.module';
import { DictModule } from './dict/dict.module';
import { LogModule } from './log/log.module';
@@ -10,7 +12,6 @@ import { OnlineModule } from './online/online.module';
import { RoleModule } from './role/role.module';
import { ServeModule } from './serve/serve.module';
import { TaskModule } from './task/task.module';
-import { UserModule } from './user/user.module';
const modules = [
UserModule,
@@ -26,8 +27,6 @@ const modules = [
@Module({
imports: [
- // forwardRef(() => WSModule),
- // forwardRef(() => AuthModule),
...modules,
RouterModule.register([
{
diff --git a/apps/api/src/modules/system/task/constant.ts b/apps/api/src/modules/system/task/constant.ts
index 82f2312..e32bf7a 100644
--- a/apps/api/src/modules/system/task/constant.ts
+++ b/apps/api/src/modules/system/task/constant.ts
@@ -1,15 +1,3 @@
-export const PermissionTask = {
- LIST: 'system:task:list',
- CREATE: 'system:task:create',
- READ: 'system:task:read',
- UPDATE: 'system:task:update',
- DELETE: 'system:task:delete',
-
- ONCE: 'system:task:once',
- START: 'system:task:start',
- STOP: 'system:task:stop',
-} as const;
-
export enum TaskStatus {
Disabled = 0,
Activited = 1,
diff --git a/apps/api/src/modules/system/task/task.controller.ts b/apps/api/src/modules/system/task/task.controller.ts
index bbc8d7e..3349cce 100644
--- a/apps/api/src/modules/system/task/task.controller.ts
+++ b/apps/api/src/modules/system/task/task.controller.ts
@@ -9,17 +9,28 @@ import {
} from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
-import { ApiResult } from '@/decorators/api-result.decorator';
-import { IdParam } from '@/decorators/id-param.decorator';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
+import { IdParam } from '@/common/decorators/id-param.decorator';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
import { Pagination } from '@/helper/paginate/pagination';
-import { Permission } from '@/modules/rbac/decorators';
+import { Permission } from '@/modules/auth/decorators/permission.decorator';
import { TaskEntity } from '@/modules/system/task/task.entity';
-import { PermissionTask } from './constant';
import { TaskDto, TaskQueryDto } from './task.dto';
import { TaskService } from './task.service';
+export const Permissions = {
+ LIST: 'system:task:list',
+ CREATE: 'system:task:create',
+ READ: 'system:task:read',
+ UPDATE: 'system:task:update',
+ DELETE: 'system:task:delete',
+
+ ONCE: 'system:task:once',
+ START: 'system:task:start',
+ STOP: 'system:task:stop',
+} as const;
+
@ApiTags('System - 任务调度模块')
@ApiSecurityAuth()
@Controller('tasks')
@@ -29,14 +40,14 @@ export class TaskController {
@Get()
@ApiOperation({ summary: '获取任务列表' })
@ApiResult({ type: [TaskEntity] })
- @Permission(PermissionTask.LIST)
+ @Permission(Permissions.LIST)
async list(@Query() dto: TaskQueryDto): Promise> {
return this.taskService.list(dto);
}
@Post()
@ApiOperation({ summary: '添加任务' })
- @Permission(PermissionTask.CREATE)
+ @Permission(Permissions.CREATE)
async create(@Body() dto: TaskDto): Promise {
const serviceCall = dto.service.split('.');
await this.taskService.checkHasMissionMeta(serviceCall[0], serviceCall[1]);
@@ -45,7 +56,7 @@ export class TaskController {
@Put(':id')
@ApiOperation({ summary: '更新任务' })
- @Permission(PermissionTask.UPDATE)
+ @Permission(Permissions.UPDATE)
async update(
@IdParam() id: number,
@Body() dto: Partial,
@@ -58,14 +69,14 @@ export class TaskController {
@Get(':id')
@ApiOperation({ summary: '查询任务详细信息' })
@ApiResult({ type: TaskEntity })
- @Permission(PermissionTask.READ)
+ @Permission(Permissions.READ)
async info(@IdParam() id: number): Promise {
return this.taskService.info(id);
}
@Delete(':id')
@ApiOperation({ summary: '删除任务' })
- @Permission(PermissionTask.DELETE)
+ @Permission(Permissions.DELETE)
async delete(@IdParam() id: number): Promise {
const task = await this.taskService.info(id);
await this.taskService.delete(task);
@@ -73,7 +84,7 @@ export class TaskController {
@Put(':id/once')
@ApiOperation({ summary: '手动执行一次任务' })
- @Permission(PermissionTask.ONCE)
+ @Permission(Permissions.ONCE)
async once(@IdParam() id: number): Promise {
const task = await this.taskService.info(id);
await this.taskService.once(task);
@@ -81,7 +92,7 @@ export class TaskController {
@Put(':id/stop')
@ApiOperation({ summary: '停止任务' })
- @Permission(PermissionTask.STOP)
+ @Permission(Permissions.STOP)
async stop(@IdParam() id: number): Promise {
const task = await this.taskService.info(id);
await this.taskService.stop(task);
@@ -89,7 +100,7 @@ export class TaskController {
@Put(':id/start')
@ApiOperation({ summary: '启动任务' })
- @Permission(PermissionTask.START)
+ @Permission(Permissions.START)
async start(@IdParam() id: number): Promise {
const task = await this.taskService.info(id);
diff --git a/apps/api/src/modules/system/task/task.service.ts b/apps/api/src/modules/system/task/task.service.ts
index 8a19b0c..9c2e974 100644
--- a/apps/api/src/modules/system/task/task.service.ts
+++ b/apps/api/src/modules/system/task/task.service.ts
@@ -3,6 +3,7 @@ import { InjectQueue } from '@nestjs/bull';
import {
BadRequestException,
Injectable,
+ Logger,
NotFoundException,
OnModuleInit,
} from '@nestjs/common';
@@ -14,12 +15,11 @@ import Redis from 'ioredis';
import { isEmpty } from 'lodash';
import { Like, Repository } from 'typeorm';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiException } from '@/exceptions/api.exception';
import { paginate } from '@/helper/paginate';
import { Pagination } from '@/helper/paginate/pagination';
-import { AppLoggerService } from '@/modules/shared/services/app-logger.service';
import { TaskEntity } from '@/modules/system/task/task.entity';
import { MISSION_DECORATOR_KEY } from '@/modules/tasks/mission.decorator';
@@ -34,6 +34,8 @@ import { TaskDto, TaskQueryDto } from './task.dto';
@Injectable()
export class TaskService implements OnModuleInit {
+ private logger = new Logger(TaskService.name);
+
constructor(
@InjectRepository(TaskEntity)
private taskRepository: Repository,
@@ -41,8 +43,6 @@ export class TaskService implements OnModuleInit {
private moduleRef: ModuleRef,
private reflector: Reflector,
@InjectRedis() private redis: Redis,
-
- private logger: AppLoggerService,
) {}
/**
@@ -313,7 +313,7 @@ export class TaskService implements OnModuleInit {
);
// 如果没有,则抛出错误
if (!hasMission) {
- throw new ApiException(ErrorEnum.INSECURE_MISSION);
+ throw new BusinessException(ErrorEnum.INSECURE_MISSION);
}
} catch (e) {
if (e instanceof UnknownElementException) {
diff --git a/apps/api/src/modules/system/user/dto/index.ts b/apps/api/src/modules/system/user/dto/index.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/apps/api/src/modules/system/user/entities/index.ts b/apps/api/src/modules/system/user/entities/index.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/apps/api/src/modules/system/user/permission.ts b/apps/api/src/modules/system/user/permission.ts
deleted file mode 100644
index 6643e0d..0000000
--- a/apps/api/src/modules/system/user/permission.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export const PermissionUser = {
- LIST: 'system:user:list',
- CREATE: 'system:user:create',
- READ: 'system:user:read',
- UPDATE: 'system:user:update',
- DELETE: 'system:user:delete',
-
- PASSWORD_UPDATE: 'system:user:password:update',
- PASSWORD_RESET: 'system:user:pass:reset',
-} as const;
diff --git a/apps/api/src/modules/tasks/jobs/email.job.ts b/apps/api/src/modules/tasks/jobs/email.job.ts
index 2413031..a71c21a 100644
--- a/apps/api/src/modules/tasks/jobs/email.job.ts
+++ b/apps/api/src/modules/tasks/jobs/email.job.ts
@@ -1,8 +1,7 @@
-import { BadRequestException, Injectable } from '@nestjs/common';
+import { BadRequestException, Injectable, Logger } from '@nestjs/common';
-import { MailerService } from '@/modules/shared/mailer/mailer.service';
+import { MailerService } from '@/shared/mailer/mailer.service';
-import { AppLoggerService } from '../../shared/services/app-logger.service';
import { Mission } from '../mission.decorator';
/**
@@ -13,7 +12,7 @@ import { Mission } from '../mission.decorator';
export class EmailJob {
constructor(
private readonly emailService: MailerService,
- private readonly logger: AppLoggerService,
+ private readonly logger: Logger,
) {}
async send(config: any): Promise {
diff --git a/apps/api/src/modules/tasks/jobs/http-request.job.ts b/apps/api/src/modules/tasks/jobs/http-request.job.ts
index c18f3ea..c0a649d 100644
--- a/apps/api/src/modules/tasks/jobs/http-request.job.ts
+++ b/apps/api/src/modules/tasks/jobs/http-request.job.ts
@@ -1,7 +1,6 @@
import { HttpService } from '@nestjs/axios';
-import { BadRequestException, Injectable } from '@nestjs/common';
+import { BadRequestException, Injectable, Logger } from '@nestjs/common';
-import { AppLoggerService } from '../../shared/services/app-logger.service';
import { Mission } from '../mission.decorator';
/**
@@ -12,7 +11,7 @@ import { Mission } from '../mission.decorator';
export class HttpRequestJob {
constructor(
private readonly httpService: HttpService,
- private readonly logger: AppLoggerService,
+ private readonly logger: Logger,
) {}
/**
diff --git a/apps/api/src/modules/apps/todo/todo.controller.ts b/apps/api/src/modules/todo/todo.controller.ts
similarity index 61%
rename from apps/api/src/modules/apps/todo/todo.controller.ts
rename to apps/api/src/modules/todo/todo.controller.ts
index c0eea39..7c53409 100644
--- a/apps/api/src/modules/apps/todo/todo.controller.ts
+++ b/apps/api/src/modules/todo/todo.controller.ts
@@ -7,23 +7,29 @@ import {
Delete,
UseGuards,
} from '@nestjs/common';
-import { ApiTags, ApiOperation, ApiExtraModels } from '@nestjs/swagger';
+import { ApiTags, ApiOperation } from '@nestjs/swagger';
-import { ApiResult } from '@/decorators/api-result.decorator';
-import { IdParam } from '@/decorators/id-param.decorator';
-import { TodoEntity } from '@/modules/apps/todo/todo.entity';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
+import { IdParam } from '@/common/decorators/id-param.decorator';
-import { Permission } from '@/modules/rbac/decorators';
-import { Resource } from '@/modules/rbac/decorators/resource.decorator';
+import { Permission } from '@/modules/auth/decorators/permission.decorator';
+import { Resource } from '@/modules/auth/decorators/resource.decorator';
-import { ResourceGuard } from '@/modules/rbac/guards/resource.guard';
+import { ResourceGuard } from '@/modules/auth/guards/resource.guard';
+import { TodoEntity } from '@/modules/todo/todo.entity';
import { TodoDto } from './todo.dto';
-import { PermissionTodo } from './todo.permission';
import { TodoService } from './todo.service';
+export const Permissions = {
+ LIST: 'todo:list',
+ CREATE: 'todo:create',
+ READ: 'todo:read',
+ UPDATE: 'todo:update',
+ DELETE: 'todo:delete',
+} as const;
+
@ApiTags('Business - Todo模块')
-@ApiExtraModels(TodoEntity)
@UseGuards(ResourceGuard)
@Controller('todo')
export class TodoController {
@@ -32,7 +38,7 @@ export class TodoController {
@Get()
@ApiOperation({ summary: '获取Todo列表' })
@ApiResult({ type: [TodoEntity] })
- @Permission(PermissionTodo.LIST)
+ @Permission(Permissions.LIST)
async list(): Promise {
return this.todoService.list();
}
@@ -40,21 +46,22 @@ export class TodoController {
@Get(':id')
@ApiOperation({ summary: '获取Todo详情' })
@ApiResult({ type: TodoEntity })
- @Permission(PermissionTodo.READ)
+ @Permission(Permissions.READ)
async info(@IdParam() id: number): Promise {
return this.todoService.detail(id);
}
@Post()
@ApiOperation({ summary: '创建Todo' })
- @Permission(PermissionTodo.CREATE)
+ @Permission(Permissions.CREATE)
async create(@Body() dto: TodoDto): Promise {
await this.todoService.create(dto);
}
@Put(':id')
@ApiOperation({ summary: '更新Todo' })
- @Permission(PermissionTodo.UPDATE)
+ @Permission(Permissions.UPDATE)
+ @Resource(TodoEntity)
async update(
@IdParam() id: number,
@Body() dto: Partial,
@@ -64,7 +71,7 @@ export class TodoController {
@Delete(':id')
@ApiOperation({ summary: '删除Todo' })
- @Permission(PermissionTodo.DELETE)
+ @Permission(Permissions.DELETE)
@Resource(TodoEntity)
async delete(@IdParam() id: number): Promise {
await this.todoService.delete(id);
diff --git a/apps/api/src/modules/apps/todo/todo.dto.ts b/apps/api/src/modules/todo/todo.dto.ts
similarity index 80%
rename from apps/api/src/modules/apps/todo/todo.dto.ts
rename to apps/api/src/modules/todo/todo.dto.ts
index 490f017..e7de581 100644
--- a/apps/api/src/modules/apps/todo/todo.dto.ts
+++ b/apps/api/src/modules/todo/todo.dto.ts
@@ -9,4 +9,4 @@ export class TodoDto {
value: string;
}
-export class TodoPageDto extends PageOptionsDto {}
+export class TodoQueryDto extends PageOptionsDto {}
diff --git a/apps/api/src/modules/apps/todo/todo.entity.ts b/apps/api/src/modules/todo/todo.entity.ts
similarity index 86%
rename from apps/api/src/modules/apps/todo/todo.entity.ts
rename to apps/api/src/modules/todo/todo.entity.ts
index 8f794bb..9a0c50c 100644
--- a/apps/api/src/modules/apps/todo/todo.entity.ts
+++ b/apps/api/src/modules/todo/todo.entity.ts
@@ -2,7 +2,7 @@ import { ApiProperty } from '@nestjs/swagger';
import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm';
import { AbstractEntity } from '@/common/entity/abstract.entity';
-import { UserEntity } from '@/modules/system/user/entities/user.entity';
+import { UserEntity } from '@/modules/user/entities/user.entity';
@Entity('todo')
export class TodoEntity extends AbstractEntity {
diff --git a/apps/api/src/modules/apps/todo/todo.module.ts b/apps/api/src/modules/todo/todo.module.ts
similarity index 100%
rename from apps/api/src/modules/apps/todo/todo.module.ts
rename to apps/api/src/modules/todo/todo.module.ts
diff --git a/apps/api/src/modules/apps/todo/todo.service.ts b/apps/api/src/modules/todo/todo.service.ts
similarity index 77%
rename from apps/api/src/modules/apps/todo/todo.service.ts
rename to apps/api/src/modules/todo/todo.service.ts
index 430a050..8bdfada 100644
--- a/apps/api/src/modules/apps/todo/todo.service.ts
+++ b/apps/api/src/modules/todo/todo.service.ts
@@ -4,9 +4,9 @@ import { Repository } from 'typeorm';
import { paginate } from '@/helper/paginate';
import { Pagination } from '@/helper/paginate/pagination';
-import { TodoEntity } from '@/modules/apps/todo/todo.entity';
+import { TodoEntity } from '@/modules/todo/todo.entity';
-import { TodoDto, TodoPageDto } from './todo.dto';
+import { TodoDto, TodoQueryDto } from './todo.dto';
@Injectable()
export class TodoService {
@@ -19,7 +19,10 @@ export class TodoService {
return this.todoRepository.find();
}
- async page({ page, pageSize }: TodoPageDto): Promise> {
+ async page({
+ page,
+ pageSize,
+ }: TodoQueryDto): Promise> {
return paginate(this.todoRepository, { page, pageSize });
}
@@ -37,8 +40,8 @@ export class TodoService {
await this.todoRepository.save(test);
}
- async update(id: number, data: Partial) {
- await this.todoRepository.update(id, data);
+ async update(id: number, dto: Partial) {
+ await this.todoRepository.update(id, dto);
}
async delete(id: number) {
diff --git a/apps/api/src/modules/tools/email/email.controller.ts b/apps/api/src/modules/tools/email/email.controller.ts
index 251e512..405a740 100644
--- a/apps/api/src/modules/tools/email/email.controller.ts
+++ b/apps/api/src/modules/tools/email/email.controller.ts
@@ -2,8 +2,8 @@ import { Body, Controller, Post } from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
-import { MailerService } from '@/modules/shared/mailer/mailer.service';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
+import { MailerService } from '@/shared/mailer/mailer.service';
import { EmailSendDto } from './email.dto';
diff --git a/apps/api/src/modules/tools/storage/storage.controller.ts b/apps/api/src/modules/tools/storage/storage.controller.ts
index cde62e7..7e732d1 100644
--- a/apps/api/src/modules/tools/storage/storage.controller.ts
+++ b/apps/api/src/modules/tools/storage/storage.controller.ts
@@ -2,12 +2,12 @@ import { Body, Controller, Get, Post, Query } from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
-import { ApiResult } from '@/decorators/api-result.decorator';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
+import { ApiResult } from '@/common/decorators/api-result.decorator';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
import { Pagination } from '@/helper/paginate/pagination';
-import { Permission } from '@/modules/rbac/decorators';
+import { Permission } from '@/modules/auth/decorators/permission.decorator';
import { StorageDeleteDto, StoragePageDto } from './storage.dto';
import { StorageInfo } from './storage.modal';
diff --git a/apps/api/src/modules/tools/storage/storage.entity.ts b/apps/api/src/modules/tools/storage/storage.entity.ts
index 1313c71..075e6d5 100644
--- a/apps/api/src/modules/tools/storage/storage.entity.ts
+++ b/apps/api/src/modules/tools/storage/storage.entity.ts
@@ -3,7 +3,7 @@ import { PrimaryGeneratedColumn, Column, Entity } from 'typeorm';
import { AbstractEntity } from '@/common/entity/abstract.entity';
-@Entity({ name: 'tool-storage' })
+@Entity({ name: 'tool_storage' })
export class Storage extends AbstractEntity {
@PrimaryGeneratedColumn()
@ApiProperty()
diff --git a/apps/api/src/modules/tools/storage/storage.service.ts b/apps/api/src/modules/tools/storage/storage.service.ts
index 075bdea..e0ece1c 100644
--- a/apps/api/src/modules/tools/storage/storage.service.ts
+++ b/apps/api/src/modules/tools/storage/storage.service.ts
@@ -5,8 +5,8 @@ import { Between, Like, Repository } from 'typeorm';
import { paginateRaw } from '@/helper/paginate';
import { PaginationTypeEnum } from '@/helper/paginate/interface';
import { Pagination } from '@/helper/paginate/pagination';
-import { UserEntity } from '@/modules/system/user/entities/user.entity';
import { Storage } from '@/modules/tools/storage/storage.entity';
+import { UserEntity } from '@/modules/user/entities/user.entity';
import { deleteFile } from '@/utils/file';
diff --git a/apps/api/src/modules/tools/tools.module.ts b/apps/api/src/modules/tools/tools.module.ts
index 6d9d88d..1a58a51 100644
--- a/apps/api/src/modules/tools/tools.module.ts
+++ b/apps/api/src/modules/tools/tools.module.ts
@@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
-import { UserEntity } from '../system/user/entities/user.entity';
+import { UserEntity } from '../user/entities/user.entity';
import { EmailController } from './email/email.controller';
import { StorageController } from './storage/storage.controller';
diff --git a/apps/api/src/modules/tools/upload/upload.controller.ts b/apps/api/src/modules/tools/upload/upload.controller.ts
index 3f2a62f..5b35cf0 100644
--- a/apps/api/src/modules/tools/upload/upload.controller.ts
+++ b/apps/api/src/modules/tools/upload/upload.controller.ts
@@ -2,10 +2,10 @@ import { MultipartFile } from '@fastify/multipart';
import { BadRequestException, Body, Controller, Post } from '@nestjs/common';
import { ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
import { AuthUser } from '@/modules/auth/decorators/auth-user.decorator';
-import { Permission } from '@/modules/rbac/decorators';
+import { Permission } from '@/modules/auth/decorators/permission.decorator';
import { UploadService } from './upload.service';
diff --git a/apps/api/src/modules/system/user/constant.ts b/apps/api/src/modules/user/constant.ts
similarity index 100%
rename from apps/api/src/modules/system/user/constant.ts
rename to apps/api/src/modules/user/constant.ts
diff --git a/apps/api/src/modules/auth/controllers/index.ts b/apps/api/src/modules/user/dto/index.ts
similarity index 100%
rename from apps/api/src/modules/auth/controllers/index.ts
rename to apps/api/src/modules/user/dto/index.ts
diff --git a/apps/api/src/modules/system/user/dto/password.dto.ts b/apps/api/src/modules/user/dto/password.dto.ts
similarity index 87%
rename from apps/api/src/modules/system/user/dto/password.dto.ts
rename to apps/api/src/modules/user/dto/password.dto.ts
index fff5dce..ba3d895 100644
--- a/apps/api/src/modules/system/user/dto/password.dto.ts
+++ b/apps/api/src/modules/user/dto/password.dto.ts
@@ -7,8 +7,8 @@ import {
MinLength,
} from 'class-validator';
-import { IsEntityExist } from '@/database/constraints/entity-exist.constraint';
-import { UserEntity } from '@/modules/system/user/entities/user.entity';
+import { UserEntity } from '@/modules/user/entities/user.entity';
+import { IsEntityExist } from '@/shared/database/constraints/entity-exist.constraint';
export class PasswordUpdateDto {
@ApiProperty({ description: '旧密码' })
diff --git a/apps/api/src/modules/system/user/dto/user.dto.ts b/apps/api/src/modules/user/dto/user.dto.ts
similarity index 94%
rename from apps/api/src/modules/system/user/dto/user.dto.ts
rename to apps/api/src/modules/user/dto/user.dto.ts
index feb155e..cbb696b 100644
--- a/apps/api/src/modules/system/user/dto/user.dto.ts
+++ b/apps/api/src/modules/user/dto/user.dto.ts
@@ -17,8 +17,8 @@ import {
import { isEmpty } from 'lodash';
import { PageOptionsDto } from '@/common/dto/page-options.dto';
-import { IsUnique } from '@/database/constraints/unique.constraint';
-import { UserEntity } from '@/modules/system/user/entities/user.entity';
+import { UserEntity } from '@/modules/user/entities/user.entity';
+import { IsUnique } from '@/shared/database/constraints/unique.constraint';
export class UserDto {
@ApiProperty({ description: '登录账号', example: 'kz-admin' })
diff --git a/apps/api/src/modules/auth/dto/index.ts b/apps/api/src/modules/user/entities/index.ts
similarity index 100%
rename from apps/api/src/modules/auth/dto/index.ts
rename to apps/api/src/modules/user/entities/index.ts
diff --git a/apps/api/src/modules/system/user/entities/user.entity.ts b/apps/api/src/modules/user/entities/user.entity.ts
similarity index 91%
rename from apps/api/src/modules/system/user/entities/user.entity.ts
rename to apps/api/src/modules/user/entities/user.entity.ts
index 44118c3..bd8ca57 100644
--- a/apps/api/src/modules/system/user/entities/user.entity.ts
+++ b/apps/api/src/modules/user/entities/user.entity.ts
@@ -14,8 +14,8 @@ import { AbstractEntity } from '@/common/entity/abstract.entity';
import { AccessTokenEntity } from '@/modules/auth/entities/access-token.entity';
-import { DeptEntity } from '../../dept/dept.entity';
-import { RoleEntity } from '../../role/role.entity';
+import { DeptEntity } from '@/modules/system/dept/dept.entity';
+import { RoleEntity } from '@/modules/system/role/role.entity';
@Entity({ name: 'sys_user' })
export class UserEntity extends AbstractEntity {
diff --git a/apps/api/src/modules/system/user/user.controller.ts b/apps/api/src/modules/user/user.controller.ts
similarity index 70%
rename from apps/api/src/modules/system/user/user.controller.ts
rename to apps/api/src/modules/user/user.controller.ts
index 33800f3..7e4d930 100644
--- a/apps/api/src/modules/system/user/user.controller.ts
+++ b/apps/api/src/modules/user/user.controller.ts
@@ -5,21 +5,30 @@ import {
Get,
Post,
Put,
- Patch,
Query,
} from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
-import { IdParam } from '@/decorators/id-param.decorator';
-import { ApiSecurityAuth } from '@/decorators/swagger.decorator';
-import { Permission } from '@/modules/rbac/decorators';
+import { IdParam } from '@/common/decorators/id-param.decorator';
+import { ApiSecurityAuth } from '@/common/decorators/swagger.decorator';
+import { Permission } from '@/modules/auth/decorators/permission.decorator';
import { MenuService } from '@/modules/system/menu/menu.service';
import { UserPasswordDto } from './dto/password.dto';
import { UserDto, UserListDto } from './dto/user.dto';
-import { PermissionUser } from './permission';
import { UserService } from './user.service';
+export const Permissions = {
+ LIST: 'system:user:list',
+ CREATE: 'system:user:create',
+ READ: 'system:user:read',
+ UPDATE: 'system:user:update',
+ DELETE: 'system:user:delete',
+
+ PASSWORD_UPDATE: 'system:user:password:update',
+ PASSWORD_RESET: 'system:user:pass:reset',
+} as const;
+
@ApiTags('System - 用户模块')
@ApiSecurityAuth()
@Controller('users')
@@ -31,28 +40,28 @@ export class UserController {
@Get()
@ApiOperation({ summary: '获取用户列表' })
- @Permission(PermissionUser.LIST)
+ @Permission(Permissions.LIST)
async list(@Query() dto: UserListDto) {
return this.userService.findAll(dto);
}
@Get(':id')
@ApiOperation({ summary: '查询用户' })
- @Permission(PermissionUser.READ)
+ @Permission(Permissions.READ)
async read(@IdParam() id: number) {
return this.userService.info(id);
}
@Post()
@ApiOperation({ summary: '新增用户' })
- @Permission(PermissionUser.CREATE)
+ @Permission(Permissions.CREATE)
async create(@Body() dto: UserDto): Promise {
await this.userService.create(dto);
}
@Put(':id')
@ApiOperation({ summary: '更新用户' })
- @Permission(PermissionUser.UPDATE)
+ @Permission(Permissions.UPDATE)
async update(
@IdParam() id: number,
@Body() dto: Partial,
@@ -63,7 +72,7 @@ export class UserController {
@Delete(':id')
@ApiOperation({ summary: '删除用户' })
- @Permission(PermissionUser.DELETE)
+ @Permission(Permissions.DELETE)
async delete(@IdParam() id: number): Promise {
await this.userService.delete([id]);
await this.userService.multiForbidden([id]);
@@ -71,7 +80,7 @@ export class UserController {
@Post(':id/password')
@ApiOperation({ summary: '更改用户密码' })
- @Patch(PermissionUser.PASSWORD_UPDATE)
+ @Permission(Permissions.PASSWORD_UPDATE)
async password(@Body() dto: UserPasswordDto): Promise {
await this.userService.forceUpdatePassword(dto.id, dto.password);
}
diff --git a/apps/api/src/modules/system/user/user.model.ts b/apps/api/src/modules/user/user.model.ts
similarity index 100%
rename from apps/api/src/modules/system/user/user.model.ts
rename to apps/api/src/modules/user/user.model.ts
diff --git a/apps/api/src/modules/system/user/user.module.ts b/apps/api/src/modules/user/user.module.ts
similarity index 75%
rename from apps/api/src/modules/system/user/user.module.ts
rename to apps/api/src/modules/user/user.module.ts
index fb08f6f..338f5d5 100644
--- a/apps/api/src/modules/system/user/user.module.ts
+++ b/apps/api/src/modules/user/user.module.ts
@@ -1,10 +1,10 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
-import { DictModule } from '../dict/dict.module';
+import { DictModule } from '../system/dict/dict.module';
-import { MenuModule } from '../menu/menu.module';
-import { RoleModule } from '../role/role.module';
+import { MenuModule } from '../system/menu/menu.module';
+import { RoleModule } from '../system/role/role.module';
import { UserEntity } from './entities/user.entity';
import { UserController } from './user.controller';
diff --git a/apps/api/src/modules/system/user/user.service.ts b/apps/api/src/modules/user/user.service.ts
similarity index 88%
rename from apps/api/src/modules/system/user/user.service.ts
rename to apps/api/src/modules/user/user.service.ts
index 46168d5..0bba5f1 100644
--- a/apps/api/src/modules/system/user/user.service.ts
+++ b/apps/api/src/modules/user/user.service.ts
@@ -7,22 +7,22 @@ import { isEmpty, isNil } from 'lodash';
import { EntityManager, Like, Repository } from 'typeorm';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { IAppConfig } from '@/config';
import { ErrorEnum } from '@/constants/error-code.constant';
import { SYS_USER_INITPASSWORD } from '@/constants/system.constant';
-import { ApiException } from '@/exceptions/api.exception';
import { paginate } from '@/helper/paginate';
import { Pagination } from '@/helper/paginate/pagination';
import { AccountUpdateDto } from '@/modules/auth/dto/account.dto';
import { RegisterDto } from '@/modules/auth/dto/auth.dto';
-import { QQService } from '@/modules/shared/qq/qq.service';
+import { QQService } from '@/shared/qq/qq.service';
import { MD5, randomValue } from '@/utils';
-import { DictService } from '../dict/dict.service';
+import { DictService } from '../system/dict/dict.service';
-import { RoleEntity } from '../role/role.entity';
+import { RoleEntity } from '../system/role/role.entity';
import { UserStatus } from './constant';
import { PasswordUpdateDto } from './dto/password.dto';
@@ -45,9 +45,16 @@ export class UserService {
private readonly configService: ConfigService,
) {}
- /**
- * 根据用户名查找已经启用的用户
- */
+ async findUserById(id: number): Promise {
+ return this.userRepository
+ .createQueryBuilder('user')
+ .where({
+ id,
+ status: UserStatus.Enabled,
+ })
+ .getOne();
+ }
+
async findUserByUserName(username: string): Promise {
return this.userRepository
.createQueryBuilder('user')
@@ -63,8 +70,15 @@ export class UserService {
* @param uid user id
*/
async getAccountInfo(uid: number): Promise {
- const user: UserEntity = await this.userRepository.findOneBy({ id: uid });
- if (isEmpty(user)) throw new ApiException(ErrorEnum.USER_NOT_FOUND);
+ const user: UserEntity = await this.userRepository
+ .createQueryBuilder('user')
+ .leftJoinAndSelect('user.roles', 'role')
+ .where(`user.id = :uid`, { uid })
+ .getOne();
+
+ if (isEmpty(user)) throw new BusinessException(ErrorEnum.USER_NOT_FOUND);
+
+ delete user?.psalt;
return user;
}
@@ -74,7 +88,7 @@ export class UserService {
*/
async updateAccountInfo(uid: number, info: AccountUpdateDto): Promise {
const user = await this.userRepository.findOneBy({ id: uid });
- if (isEmpty(user)) throw new ApiException(ErrorEnum.USER_NOT_FOUND);
+ if (isEmpty(user)) throw new BusinessException(ErrorEnum.USER_NOT_FOUND);
const data = {
...(info.nickname ? { nickname: info.nickname } : null),
@@ -101,12 +115,12 @@ export class UserService {
async updatePassword(uid: number, dto: PasswordUpdateDto): Promise {
const user = await this.userRepository.findOneBy({ id: uid });
if (isEmpty(user)) {
- throw new ApiException(ErrorEnum.USER_NOT_FOUND);
+ throw new BusinessException(ErrorEnum.USER_NOT_FOUND);
}
const comparePassword = MD5(`${dto.oldPassword}${user.psalt}`);
// 原密码不一致,不允许更改
if (user.password !== comparePassword) {
- throw new ApiException(ErrorEnum.PASSWORD_MISMATCH);
+ throw new BusinessException(ErrorEnum.PASSWORD_MISMATCH);
}
const password = MD5(`${dto.newPassword}${user.psalt}`);
await this.userRepository.update({ id: uid }, { password });
@@ -138,7 +152,7 @@ export class UserService {
username,
});
if (!isEmpty(exists)) {
- throw new ApiException(ErrorEnum.SYSTEM_USER_EXISTS);
+ throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS);
}
await this.entityManager.transaction(async (manager) => {
@@ -329,7 +343,7 @@ export class UserService {
async exist(username: string) {
const user = await this.userRepository.findOneBy({ username });
if (isNil(user)) {
- throw new ApiException(ErrorEnum.SYSTEM_USER_EXISTS);
+ throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS);
}
return true;
}
@@ -341,7 +355,8 @@ export class UserService {
const exists = await this.userRepository.findOneBy({
username,
});
- if (!isEmpty(exists)) throw new ApiException(ErrorEnum.SYSTEM_USER_EXISTS);
+ if (!isEmpty(exists))
+ throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS);
await this.entityManager.transaction(async (manager) => {
const salt = randomValue(32);
diff --git a/apps/api/src/utils/setup-swagger.ts b/apps/api/src/setup-swagger.ts
similarity index 50%
rename from apps/api/src/utils/setup-swagger.ts
rename to apps/api/src/setup-swagger.ts
index db9c5db..902c851 100644
--- a/apps/api/src/utils/setup-swagger.ts
+++ b/apps/api/src/setup-swagger.ts
@@ -2,23 +2,24 @@ import { INestApplication, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
-import { AbstractEntity } from '../common/entity/abstract.entity';
-import { ResOp, TreeResult } from '../common/model/response.model';
-import { IAppConfig, ISwaggerConfig } from '../config';
-import { API_SECURITY_AUTH } from '../decorators/swagger.decorator';
-import { Pagination } from '../helper/paginate/pagination';
+import { API_SECURITY_AUTH } from './common/decorators/swagger.decorator';
+import { AbstractEntity } from './common/entity/abstract.entity';
+import { ResOp, TreeResult } from './common/model/response.model';
+import { IAppConfig, ISwaggerConfig } from './config';
+import { Pagination } from './helper/paginate/pagination';
export function setupSwagger(
app: INestApplication,
configService: ConfigService,
): void {
+ const { name, port } = configService.get('app');
const { enable, path } = configService.get('swagger');
if (!enable) return;
const documentBuilder = new DocumentBuilder()
- .setTitle(`${configService.get('app').name}`)
- .setDescription(`${configService.get('app').name} API document`)
+ .setTitle(name)
+ .setDescription(`${name} API document`)
.setVersion('1.0');
// auth security
@@ -36,23 +37,7 @@ export function setupSwagger(
SwaggerModule.setup(path, app, document);
- // 导入 ApiFox 忽略全局前缀
- const documentApiFox = SwaggerModule.createDocument(
- app,
- documentBuilder.build(),
- {
- ignoreGlobalPrefix: true,
- extraModels: [AbstractEntity, ResOp, Pagination, TreeResult],
- },
- );
-
- SwaggerModule.setup(`${path}-fox`, app, documentApiFox);
-
// started log
const logger = new Logger('SwaggerModule');
- logger.log(
- `Document running on http://127.0.0.1:${
- configService.get('app').port
- }/${path}`,
- );
+ logger.log(`Document running on http://127.0.0.1:${port}/${path}`);
}
diff --git a/apps/api/src/database/constraints/entity-exist.constraint.ts b/apps/api/src/shared/database/constraints/entity-exist.constraint.ts
similarity index 100%
rename from apps/api/src/database/constraints/entity-exist.constraint.ts
rename to apps/api/src/shared/database/constraints/entity-exist.constraint.ts
diff --git a/apps/api/src/database/constraints/unique.constraint.ts b/apps/api/src/shared/database/constraints/unique.constraint.ts
similarity index 94%
rename from apps/api/src/database/constraints/unique.constraint.ts
rename to apps/api/src/shared/database/constraints/unique.constraint.ts
index b031732..3714cee 100644
--- a/apps/api/src/database/constraints/unique.constraint.ts
+++ b/apps/api/src/shared/database/constraints/unique.constraint.ts
@@ -21,7 +21,7 @@ type Condition = {
@ValidatorConstraint({ name: 'entityItemUnique', async: true })
@Injectable()
export class UniqueConstraint implements ValidatorConstraintInterface {
- constructor(private dataSource: DataSource) { }
+ constructor(private dataSource: DataSource) {}
async validate(value: any, args: ValidationArguments) {
// 获取要验证的模型和字段
@@ -31,9 +31,9 @@ export class UniqueConstraint implements ValidatorConstraintInterface {
const condition = ('entity' in args.constraints[0]
? merge(config, args.constraints[0])
: {
- ...config,
- entity: args.constraints[0],
- }) as unknown as Required;
+ ...config,
+ entity: args.constraints[0],
+ }) as unknown as Required;
if (!condition.entity) return false;
try {
// 查询是否存在数据,如果已经存在则验证失败
diff --git a/apps/api/src/database/database.module.ts b/apps/api/src/shared/database/database.module.ts
similarity index 90%
rename from apps/api/src/database/database.module.ts
rename to apps/api/src/shared/database/database.module.ts
index ddec799..c24527c 100644
--- a/apps/api/src/database/database.module.ts
+++ b/apps/api/src/shared/database/database.module.ts
@@ -6,7 +6,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { LoggerOptions } from 'typeorm';
import { IDatabaseConfig } from '@/config';
-import { env } from '@/config/env';
+import { env } from '@/global/env';
import { EntityExistConstraint } from './constraints/entity-exist.constraint';
import { UniqueConstraint } from './constraints/unique.constraint';
@@ -21,6 +21,7 @@ const providers = [EntityExistConstraint, UniqueConstraint];
let loggerOptions: LoggerOptions = env('DB_LOGGING') as 'all';
try {
+ // 解析成 js 数组 ['error']
loggerOptions = JSON.parse(loggerOptions);
} catch {
// ignore
@@ -39,4 +40,4 @@ const providers = [EntityExistConstraint, UniqueConstraint];
providers,
exports: providers,
})
-export class AppDatabaseModule {}
+export class DatabaseModule {}
diff --git a/apps/api/src/database/typeorm-logger.ts b/apps/api/src/shared/database/typeorm-logger.ts
similarity index 96%
rename from apps/api/src/database/typeorm-logger.ts
rename to apps/api/src/shared/database/typeorm-logger.ts
index fc675b1..5b65822 100644
--- a/apps/api/src/database/typeorm-logger.ts
+++ b/apps/api/src/shared/database/typeorm-logger.ts
@@ -2,7 +2,7 @@ import { Logger } from '@nestjs/common';
import { Logger as ITypeORMLogger, LoggerOptions, QueryRunner } from 'typeorm';
export class TypeORMLogger implements ITypeORMLogger {
- private readonly logger = new Logger(TypeORMLogger.name);
+ private logger = new Logger(TypeORMLogger.name);
constructor(private options: LoggerOptions) {}
@@ -79,6 +79,8 @@ export class TypeORMLogger implements ITypeORMLogger {
case 'warn':
this.logger.warn(message);
break;
+ default:
+ break;
}
}
diff --git a/apps/api/src/modules/shared/ip/ip.service.ts b/apps/api/src/shared/ip/ip.service.ts
similarity index 100%
rename from apps/api/src/modules/shared/ip/ip.service.ts
rename to apps/api/src/shared/ip/ip.service.ts
diff --git a/apps/api/src/shared/logger/logger.module.ts b/apps/api/src/shared/logger/logger.module.ts
new file mode 100644
index 0000000..664d4fc
--- /dev/null
+++ b/apps/api/src/shared/logger/logger.module.ts
@@ -0,0 +1,9 @@
+import { Module } from '@nestjs/common';
+
+import { MyLogger } from './logger.service';
+
+@Module({
+ providers: [MyLogger],
+ exports: [MyLogger],
+})
+export class LoggerModule {}
diff --git a/apps/api/src/shared/logger/logger.service.ts b/apps/api/src/shared/logger/logger.service.ts
new file mode 100644
index 0000000..e1f2d6a
--- /dev/null
+++ b/apps/api/src/shared/logger/logger.service.ts
@@ -0,0 +1,117 @@
+import {
+ ConsoleLogger,
+ ConsoleLoggerOptions,
+ Injectable,
+} from '@nestjs/common';
+
+import { ConfigService } from '@nestjs/config';
+import type { Logger as WinstonLogger } from 'winston';
+
+import { createLogger, format, transports, config } from 'winston';
+
+import 'winston-daily-rotate-file';
+
+export enum LogLevel {
+ ERROR = 'error',
+ WARN = 'warn',
+ INFO = 'info',
+ DEBUG = 'debug',
+ VERBOSE = 'verbose',
+}
+
+@Injectable()
+export class MyLogger extends ConsoleLogger {
+ private winstonLogger: WinstonLogger;
+
+ constructor(
+ context: string,
+ options: ConsoleLoggerOptions,
+ private configService: ConfigService,
+ ) {
+ super(context, options);
+ this.initWinston();
+ }
+
+ protected get level(): LogLevel {
+ return this.configService.get('app.logger.level') as LogLevel;
+ }
+
+ protected get maxFiles(): number {
+ return this.configService.get('app.logger.maxFiles');
+ }
+
+ protected initWinston(): void {
+ this.winstonLogger = createLogger({
+ levels: config.npm.levels,
+ format: format.combine(
+ format.errors({ stack: true }),
+ format.timestamp(),
+ format.json(),
+ ),
+ transports: [
+ new transports.DailyRotateFile({
+ level: this.level,
+ filename: 'logs/app.%DATE%.log',
+ datePattern: 'YYYY-MM-DD',
+ maxFiles: this.maxFiles,
+ format: format.combine(format.timestamp(), format.json()),
+ auditFile: 'logs/.audit/app.json',
+ }),
+ new transports.DailyRotateFile({
+ level: LogLevel.ERROR,
+ filename: 'logs/app-error.%DATE%.log',
+ datePattern: 'YYYY-MM-DD',
+ maxFiles: this.maxFiles,
+ format: format.combine(format.timestamp(), format.json()),
+ auditFile: 'logs/.audit/app-error.json',
+ }),
+ ],
+ });
+
+ // if (isDev) {
+ // this.winstonLogger.add(
+ // new transports.Console({
+ // level: this.level,
+ // format: format.combine(
+ // format.simple(),
+ // format.colorize({ all: true }),
+ // ),
+ // }),
+ // );
+ // }
+ }
+
+ verbose(message: any, context?: string): void {
+ super.verbose.apply(this, [message, context]);
+
+ this.winstonLogger.log(LogLevel.VERBOSE, message, { context });
+ }
+
+ debug(message: any, context?: string): void {
+ super.debug.apply(this, [message, context]);
+
+ this.winstonLogger.log(LogLevel.DEBUG, message, { context });
+ }
+
+ log(message: any, context?: string): void {
+ super.log.apply(this, [message, context]);
+
+ this.winstonLogger.log(LogLevel.INFO, message, { context });
+ }
+
+ warn(message: any, context?: string): void {
+ super.warn.apply(this, [message, context]);
+
+ this.winstonLogger.log(LogLevel.WARN, message);
+ }
+
+ error(message: any, stack?: string, context?: string): void {
+ super.error.apply(this, [message, stack, context]);
+
+ const hasStack = !!context;
+ this.winstonLogger.log(LogLevel.ERROR, {
+ context: hasStack ? context : stack,
+ message: hasStack ? new Error(message) : message,
+ });
+ }
+}
diff --git a/apps/api/src/modules/shared/mailer/mailer.service.ts b/apps/api/src/shared/mailer/mailer.service.ts
similarity index 85%
rename from apps/api/src/modules/shared/mailer/mailer.service.ts
rename to apps/api/src/shared/mailer/mailer.service.ts
index 57ed755..430b8aa 100644
--- a/apps/api/src/modules/shared/mailer/mailer.service.ts
+++ b/apps/api/src/shared/mailer/mailer.service.ts
@@ -7,9 +7,9 @@ import dayjs from 'dayjs';
import Redis from 'ioredis';
+import { BusinessException } from '@/common/exceptions/biz.exception';
import { IAppConfig } from '@/config';
import { ErrorEnum } from '@/constants/error-code.constant';
-import { ApiException } from '@/exceptions/api.exception';
import { randomValue } from '@/utils';
@@ -58,7 +58,7 @@ export class MailerService {
);
} catch (error) {
console.log(error);
- throw new ApiException(ErrorEnum.VERIFICATION_CODE_SEND_FAILED);
+ throw new BusinessException(ErrorEnum.VERIFICATION_CODE_SEND_FAILED);
}
return {
@@ -70,7 +70,7 @@ export class MailerService {
async checkCode(to, code) {
const ret = await this.redis.get(`captcha:${to}`);
if (ret !== code) {
- throw new ApiException(ErrorEnum.INVALID_VERIFICATION_CODE);
+ throw new BusinessException(ErrorEnum.INVALID_VERIFICATION_CODE);
}
await this.redis.del(`captcha:${to}`);
}
@@ -80,11 +80,11 @@ export class MailerService {
// ip限制
const ipLimit = await this.redis.get(`ip:${ip}:send:limit`);
- if (ipLimit) throw new ApiException(ErrorEnum.TOO_MANY_REQUESTS);
+ if (ipLimit) throw new BusinessException(ErrorEnum.TOO_MANY_REQUESTS);
// 1分钟最多接收1条
const limit = await this.redis.get(`captcha:${to}:limit`);
- if (limit) throw new ApiException(ErrorEnum.TOO_MANY_REQUESTS);
+ if (limit) throw new BusinessException(ErrorEnum.TOO_MANY_REQUESTS);
// 1天一个邮箱最多接收5条
let limitCountOfDay: string | number = await this.redis.get(
@@ -92,7 +92,9 @@ export class MailerService {
);
limitCountOfDay = limitCountOfDay ? Number(limitCountOfDay) : 0;
if (limitCountOfDay > LIMIT_TIME)
- throw new ApiException(ErrorEnum.MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY);
+ throw new BusinessException(
+ ErrorEnum.MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY,
+ );
// 1天一个ip最多发送5条
let ipLimitCountOfDay: string | number = await this.redis.get(
@@ -100,7 +102,9 @@ export class MailerService {
);
ipLimitCountOfDay = ipLimitCountOfDay ? Number(ipLimitCountOfDay) : 0;
if (ipLimitCountOfDay > LIMIT_TIME)
- throw new ApiException(ErrorEnum.MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY);
+ throw new BusinessException(
+ ErrorEnum.MAXIMUM_FIVE_VERIFICATION_CODES_PER_DAY,
+ );
}
async log(to: string, code: string, ip: string) {
diff --git a/apps/api/src/modules/shared/qq/qq.service.ts b/apps/api/src/shared/qq/qq.service.ts
similarity index 100%
rename from apps/api/src/modules/shared/qq/qq.service.ts
rename to apps/api/src/shared/qq/qq.service.ts
diff --git a/apps/api/src/modules/shared/shared.module.ts b/apps/api/src/shared/shared.module.ts
similarity index 85%
rename from apps/api/src/modules/shared/shared.module.ts
rename to apps/api/src/shared/shared.module.ts
index a1acf73..0f6c982 100644
--- a/apps/api/src/modules/shared/shared.module.ts
+++ b/apps/api/src/shared/shared.module.ts
@@ -9,22 +9,22 @@ import { MailerModule } from '@nestjs-modules/mailer';
import { IMailerConfig, IRedisConfig } from '@/config';
import { IpService } from './ip/ip.service';
+import { LoggerModule } from './logger/logger.module';
import { MailerService } from './mailer/mailer.service';
import { QQService } from './qq/qq.service';
-import { AppLoggerService } from './services/app-logger.service';
-const providers = [AppLoggerService, MailerService, IpService, QQService];
+const providers = [MailerService, IpService, QQService];
@Global()
@Module({
imports: [
+ // logger
+ LoggerModule,
// http
HttpModule,
// redis cache
CacheModule.register({
isGlobal: true,
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
// store: redisStore,
// host: 'localhost',
@@ -33,8 +33,8 @@ const providers = [AppLoggerService, MailerService, IpService, QQService];
// rate limit
ThrottlerModule.forRoot([
{
+ limit: 3,
ttl: 60000,
- limit: 5,
},
]),
// redis
diff --git a/package.json b/package.json
index b83793c..de7c4c1 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,6 @@
"reinstall": "rimraf pnpm-lock.yaml && rimraf package.lock.json && rimraf node_modules && npm run bootstrap"
},
"devDependencies": {
- "@kuizuo/eslint-config-ts": "^1.0.1",
"cross-env": "7.0.3",
"prettier": "^3.0.3",
"prettier-plugin-packagejson": "^2.4.6",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4684842..af428bc 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,9 +8,6 @@ importers:
.:
devDependencies:
- '@kuizuo/eslint-config-ts':
- specifier: ^1.0.1
- version: 1.0.1(eslint@8.53.0)(typescript@5.2.2)
cross-env:
specifier: 7.0.3
version: 7.0.3
@@ -249,6 +246,9 @@ importers:
apps/api:
dependencies:
+ '@fastify/cookie':
+ specifier: ^9.1.0
+ version: 9.1.0
'@fastify/multipart':
specifier: ^8.0.0
version: 8.0.0
@@ -360,9 +360,6 @@ importers:
lodash:
specifier: ^4.17.21
version: 4.17.21
- log4js:
- specifier: ^6.9.1
- version: 6.9.1
mysql:
specifier: ^2.18.1
version: 2.18.1
@@ -408,6 +405,12 @@ importers:
ua-parser-js:
specifier: ^1.0.37
version: 1.0.37
+ winston:
+ specifier: ^3.11.0
+ version: 3.11.0
+ winston-daily-rotate-file:
+ specifier: ^4.7.1
+ version: 4.7.1(winston@3.11.0)
devDependencies:
'@compodoc/compodoc':
specifier: ^1.1.22
@@ -2137,6 +2140,11 @@ packages:
dev: true
optional: true
+ /@colors/colors@1.6.0:
+ resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
+ engines: {node: '>=0.1.90'}
+ dev: false
+
/@commitlint/cli@18.2.0(typescript@5.2.2):
resolution: {integrity: sha512-F/DCG791kMFmWg5eIdogakuGeg4OiI2kD430ed1a1Hh3epvrJdeIAgcGADAMIOmF+m0S1+VlIYUKG2dvQQ1Izw==}
engines: {node: '>=v18'}
@@ -2434,6 +2442,14 @@ packages:
resolution: {integrity: sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ==}
engines: {node: '>=10'}
+ /@dabh/diagnostics@2.0.3:
+ resolution: {integrity: sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==}
+ dependencies:
+ colorspace: 1.1.4
+ enabled: 2.0.0
+ kuler: 2.0.0
+ dev: false
+
/@emotion/hash@0.9.1:
resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==}
@@ -2903,6 +2919,13 @@ packages:
text-decoding: 1.0.0
dev: false
+ /@fastify/cookie@9.1.0:
+ resolution: {integrity: sha512-w/LlQjj7cmYlQNhEKNm4jQoLkFXCL73kFu1Jy3aL7IFbYEojEKur0f7ieCKUxBBaU65tpaWC83UM8xW7AzY6uw==}
+ dependencies:
+ cookie: 0.5.0
+ fastify-plugin: 4.5.0
+ dev: false
+
/@fastify/cors@8.4.0:
resolution: {integrity: sha512-MlVvMTenltToByTpLwlWtO+7dQ3l2J+1OpmGrx9JpSNWo1d+dhfNCOi23zHhxdFhtpDzfwGwCsKu9DTeG7k7nQ==}
dependencies:
@@ -3617,49 +3640,6 @@ packages:
resolution: {integrity: sha512-CGZ9HGmHGTcGnU8CDQm7RR7hZgxLyRHTIFpS1FDCQkVaipdL/poq72ibpKqqQflomgKRCYV6GReP7ZXEZeDx1w==}
dev: true
- /@kuizuo/eslint-config-basic@1.0.1(@typescript-eslint/parser@5.57.1)(eslint@8.53.0)(typescript@5.2.2):
- resolution: {integrity: sha512-fFzBKBQtkew18JKhnSCVtwIv0oOI7D3pOgapo71MiZ9VUzSReVD7OkhTaUxRx3n/Iv4oPVO//lXAUEk6xy4aCA==}
- peerDependencies:
- eslint: '>=7.4.0'
- dependencies:
- eslint: 8.53.0
- eslint-plugin-eslint-comments: 3.2.0(eslint@8.53.0)
- eslint-plugin-html: 7.1.0
- eslint-plugin-import: 2.29.0(@typescript-eslint/parser@5.57.1)(eslint@8.53.0)
- eslint-plugin-jsonc: 2.9.0(eslint@8.53.0)
- eslint-plugin-kuizuo: 1.0.1(eslint@8.53.0)(typescript@5.2.2)
- eslint-plugin-markdown: 3.0.0(eslint@8.53.0)
- eslint-plugin-n: 15.7.0(eslint@8.53.0)
- eslint-plugin-promise: 6.1.1(eslint@8.53.0)
- eslint-plugin-unicorn: 43.0.2(eslint@8.53.0)
- eslint-plugin-yml: 1.8.0(eslint@8.53.0)
- jsonc-eslint-parser: 2.3.0
- yaml-eslint-parser: 1.2.2
- transitivePeerDependencies:
- - '@typescript-eslint/parser'
- - eslint-import-resolver-typescript
- - eslint-import-resolver-webpack
- - supports-color
- - typescript
- dev: true
-
- /@kuizuo/eslint-config-ts@1.0.1(eslint@8.53.0)(typescript@5.2.2):
- resolution: {integrity: sha512-2hROWJ2k7wBlRJrXQSSYKjFbLACMkE7lFRJGMY9MOHt4sNJECQnRhz8WboS4JQrlW5pbdS8hA0qnvyoda5hpYg==}
- peerDependencies:
- eslint: '>=7.4.0'
- typescript: '>=3.9'
- dependencies:
- '@kuizuo/eslint-config-basic': 1.0.1(@typescript-eslint/parser@5.57.1)(eslint@8.53.0)(typescript@5.2.2)
- '@typescript-eslint/eslint-plugin': 5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.53.0)(typescript@5.2.2)
- '@typescript-eslint/parser': 5.57.1(eslint@8.53.0)(typescript@5.2.2)
- eslint: 8.53.0
- typescript: 5.2.2
- transitivePeerDependencies:
- - eslint-import-resolver-typescript
- - eslint-import-resolver-webpack
- - supports-color
- dev: true
-
/@liaoliaots/nestjs-redis@9.0.5(@nestjs/common@10.2.8)(@nestjs/core@10.2.8)(ioredis@5.3.2):
resolution: {integrity: sha512-nPcGLj0zW4mEsYtQYfWx3o7PmrMjuzFk6+t/g2IRopAeWWUZZ/5nIJ4KTKiz/3DJEUkbX8PZqB+dOhklGF0SVA==}
engines: {node: '>=12.22.0'}
@@ -4746,12 +4726,6 @@ packages:
resolution: {integrity: sha512-uKRI5QORDnrGFYgcdAVnHvEIvEZ8noTpP/Bg+HeUzZghwinDlIS87DEenV5r1YoOF9G4x600YsUXLWZ19rmTmg==}
dev: false
- /@types/mdast@3.0.12:
- resolution: {integrity: sha512-DT+iNIRNX884cx0/Q1ja7NyUPpZuv0KPyL5rGNxm1WC1OtHstl7n4Jb7nk+xacNShQMbczJjt8uFzznpp6kYBg==}
- dependencies:
- '@types/unist': 2.0.7
- dev: true
-
/@types/mime@1.3.2:
resolution: {integrity: sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==}
dev: true
@@ -4883,14 +4857,14 @@ packages:
'@types/estree': 1.0.1
dev: true
+ /@types/triple-beam@1.3.5:
+ resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==}
+ dev: false
+
/@types/ua-parser-js@0.7.39:
resolution: {integrity: sha512-P/oDfpofrdtF5xw433SPALpdSchtJmY7nsJItf8h3KXqOslkbySh8zq4dSWXH2oTjRvJ5PczVEoCZPow6GicLg==}
dev: true
- /@types/unist@2.0.7:
- resolution: {integrity: sha512-cputDpIbFgLUaGQn6Vqg3/YsJwxUwHLO13v3i5ouxT4lat0khip9AEWxtERujXV9wxIB1EyF97BSJFt6vpdI8g==}
- dev: true
-
/@types/validator@13.7.17:
resolution: {integrity: sha512-aqayTNmeWrZcvnG2MG9eGYI6b7S5fl+yKgPs6bAjOTwPS316R5SxBGKvtSExfyoJU7pIeHJfsHI0Ji41RVMkvQ==}
@@ -4917,34 +4891,6 @@ packages:
'@types/yargs-parser': 21.0.0
dev: true
- /@typescript-eslint/eslint-plugin@5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.53.0)(typescript@5.2.2):
- resolution: {integrity: sha512-1MeobQkQ9tztuleT3v72XmY0XuKXVXusAhryoLuU5YZ+mXoYKZP9SQ7Flulh1NX4DTjpGTc2b/eMu4u7M7dhnQ==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- peerDependencies:
- '@typescript-eslint/parser': ^5.0.0
- eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
- typescript: '*'
- peerDependenciesMeta:
- typescript:
- optional: true
- dependencies:
- '@eslint-community/regexpp': 4.10.0
- '@typescript-eslint/parser': 5.57.1(eslint@8.53.0)(typescript@5.2.2)
- '@typescript-eslint/scope-manager': 5.57.1
- '@typescript-eslint/type-utils': 5.57.1(eslint@8.53.0)(typescript@5.2.2)
- '@typescript-eslint/utils': 5.57.1(eslint@8.53.0)(typescript@5.2.2)
- debug: 4.3.4
- eslint: 8.53.0
- grapheme-splitter: 1.0.4
- ignore: 5.2.4
- natural-compare-lite: 1.4.0
- semver: 7.5.4
- tsutils: 3.21.0(typescript@5.2.2)
- typescript: 5.2.2
- transitivePeerDependencies:
- - supports-color
- dev: true
-
/@typescript-eslint/eslint-plugin@6.10.0(@typescript-eslint/parser@6.10.0)(eslint@8.53.0)(typescript@5.2.2):
resolution: {integrity: sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -4974,26 +4920,6 @@ packages:
- supports-color
dev: true
- /@typescript-eslint/parser@5.57.1(eslint@8.53.0)(typescript@5.2.2):
- resolution: {integrity: sha512-hlA0BLeVSA/wBPKdPGxoVr9Pp6GutGoY380FEhbVi0Ph4WNe8kLvqIRx76RSQt1lynZKfrXKs0/XeEk4zZycuA==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- peerDependencies:
- eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
- typescript: '*'
- peerDependenciesMeta:
- typescript:
- optional: true
- dependencies:
- '@typescript-eslint/scope-manager': 5.57.1
- '@typescript-eslint/types': 5.57.1
- '@typescript-eslint/typescript-estree': 5.57.1(typescript@5.2.2)
- debug: 4.3.4
- eslint: 8.53.0
- typescript: 5.2.2
- transitivePeerDependencies:
- - supports-color
- dev: true
-
/@typescript-eslint/parser@6.10.0(eslint@8.53.0)(typescript@5.2.2):
resolution: {integrity: sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -5015,22 +4941,6 @@ packages:
- supports-color
dev: true
- /@typescript-eslint/scope-manager@5.57.1:
- resolution: {integrity: sha512-N/RrBwEUKMIYxSKl0oDK5sFVHd6VI7p9K5MyUlVYAY6dyNb/wHUqndkTd3XhpGlXgnQsBkRZuu4f9kAHghvgPw==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- dependencies:
- '@typescript-eslint/types': 5.57.1
- '@typescript-eslint/visitor-keys': 5.57.1
- dev: true
-
- /@typescript-eslint/scope-manager@5.62.0:
- resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- dependencies:
- '@typescript-eslint/types': 5.62.0
- '@typescript-eslint/visitor-keys': 5.62.0
- dev: true
-
/@typescript-eslint/scope-manager@6.10.0:
resolution: {integrity: sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -5039,26 +4949,6 @@ packages:
'@typescript-eslint/visitor-keys': 6.10.0
dev: true
- /@typescript-eslint/type-utils@5.57.1(eslint@8.53.0)(typescript@5.2.2):
- resolution: {integrity: sha512-/RIPQyx60Pt6ga86hKXesXkJ2WOS4UemFrmmq/7eOyiYjYv/MUSHPlkhU6k9T9W1ytnTJueqASW+wOmW4KrViw==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- peerDependencies:
- eslint: '*'
- typescript: '*'
- peerDependenciesMeta:
- typescript:
- optional: true
- dependencies:
- '@typescript-eslint/typescript-estree': 5.57.1(typescript@5.2.2)
- '@typescript-eslint/utils': 5.57.1(eslint@8.53.0)(typescript@5.2.2)
- debug: 4.3.4
- eslint: 8.53.0
- tsutils: 3.21.0(typescript@5.2.2)
- typescript: 5.2.2
- transitivePeerDependencies:
- - supports-color
- dev: true
-
/@typescript-eslint/type-utils@6.10.0(eslint@8.53.0)(typescript@5.2.2):
resolution: {integrity: sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -5079,63 +4969,11 @@ packages:
- supports-color
dev: true
- /@typescript-eslint/types@5.57.1:
- resolution: {integrity: sha512-bSs4LOgyV3bJ08F5RDqO2KXqg3WAdwHCu06zOqcQ6vqbTJizyBhuh1o1ImC69X4bV2g1OJxbH71PJqiO7Y1RuA==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- dev: true
-
- /@typescript-eslint/types@5.62.0:
- resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- dev: true
-
/@typescript-eslint/types@6.10.0:
resolution: {integrity: sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==}
engines: {node: ^16.0.0 || >=18.0.0}
dev: true
- /@typescript-eslint/typescript-estree@5.57.1(typescript@5.2.2):
- resolution: {integrity: sha512-A2MZqD8gNT0qHKbk2wRspg7cHbCDCk2tcqt6ScCFLr5Ru8cn+TCfM786DjPhqwseiS+PrYwcXht5ztpEQ6TFTw==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- peerDependencies:
- typescript: '*'
- peerDependenciesMeta:
- typescript:
- optional: true
- dependencies:
- '@typescript-eslint/types': 5.57.1
- '@typescript-eslint/visitor-keys': 5.57.1
- debug: 4.3.4
- globby: 11.1.0
- is-glob: 4.0.3
- semver: 7.5.4
- tsutils: 3.21.0(typescript@5.2.2)
- typescript: 5.2.2
- transitivePeerDependencies:
- - supports-color
- dev: true
-
- /@typescript-eslint/typescript-estree@5.62.0(typescript@5.2.2):
- resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- peerDependencies:
- typescript: '*'
- peerDependenciesMeta:
- typescript:
- optional: true
- dependencies:
- '@typescript-eslint/types': 5.62.0
- '@typescript-eslint/visitor-keys': 5.62.0
- debug: 4.3.4
- globby: 11.1.0
- is-glob: 4.0.3
- semver: 7.5.4
- tsutils: 3.21.0(typescript@5.2.2)
- typescript: 5.2.2
- transitivePeerDependencies:
- - supports-color
- dev: true
-
/@typescript-eslint/typescript-estree@6.10.0(typescript@5.2.2):
resolution: {integrity: sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -5157,46 +4995,6 @@ packages:
- supports-color
dev: true
- /@typescript-eslint/utils@5.57.1(eslint@8.53.0)(typescript@5.2.2):
- resolution: {integrity: sha512-kN6vzzf9NkEtawECqze6v99LtmDiUJCVpvieTFA1uL7/jDghiJGubGZ5csicYHU1Xoqb3oH/R5cN5df6W41Nfg==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- peerDependencies:
- eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
- dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
- '@types/json-schema': 7.0.12
- '@types/semver': 7.5.0
- '@typescript-eslint/scope-manager': 5.57.1
- '@typescript-eslint/types': 5.57.1
- '@typescript-eslint/typescript-estree': 5.57.1(typescript@5.2.2)
- eslint: 8.53.0
- eslint-scope: 5.1.1
- semver: 7.5.4
- transitivePeerDependencies:
- - supports-color
- - typescript
- dev: true
-
- /@typescript-eslint/utils@5.62.0(eslint@8.53.0)(typescript@5.2.2):
- resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- peerDependencies:
- eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
- dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
- '@types/json-schema': 7.0.12
- '@types/semver': 7.5.0
- '@typescript-eslint/scope-manager': 5.62.0
- '@typescript-eslint/types': 5.62.0
- '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.2.2)
- eslint: 8.53.0
- eslint-scope: 5.1.1
- semver: 7.5.4
- transitivePeerDependencies:
- - supports-color
- - typescript
- dev: true
-
/@typescript-eslint/utils@6.10.0(eslint@8.53.0)(typescript@5.2.2):
resolution: {integrity: sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -5216,22 +5014,6 @@ packages:
- typescript
dev: true
- /@typescript-eslint/visitor-keys@5.57.1:
- resolution: {integrity: sha512-RjQrAniDU0CEk5r7iphkm731zKlFiUjvcBS2yHAg8WWqFMCaCrD0rKEVOMUyMMcbGPZ0bPp56srkGWrgfZqLRA==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- dependencies:
- '@typescript-eslint/types': 5.57.1
- eslint-visitor-keys: 3.4.3
- dev: true
-
- /@typescript-eslint/visitor-keys@5.62.0:
- resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- dependencies:
- '@typescript-eslint/types': 5.62.0
- eslint-visitor-keys: 3.4.3
- dev: true
-
/@typescript-eslint/visitor-keys@6.10.0:
resolution: {integrity: sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==}
engines: {node: ^16.0.0 || >=18.0.0}
@@ -6789,12 +6571,6 @@ packages:
engines: {node: '>=6'}
dev: true
- /builtins@5.0.1:
- resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==}
- dependencies:
- semver: 7.5.4
- dev: true
-
/bull@4.11.4:
resolution: {integrity: sha512-6rPnFkUbN/eWhzGF65mcYM2HWDl2rp0fTidZ8en64Zwplioe/QxpdiWfLLtXX4Yy25piPly4f96wHR0NquiyyQ==}
engines: {node: '>=12'}
@@ -6985,14 +6761,6 @@ packages:
resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
engines: {node: '>=10'}
- /character-entities-legacy@1.1.4:
- resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==}
- dev: true
-
- /character-entities@1.2.4:
- resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==}
- dev: true
-
/character-parser@2.2.0:
resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==}
requiresBuild: true
@@ -7000,10 +6768,6 @@ packages:
is-regex: 1.1.4
dev: false
- /character-reference-invalid@1.1.4:
- resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==}
- dev: true
-
/chardet@0.7.0:
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
dev: true
@@ -7105,13 +6869,6 @@ packages:
source-map: 0.6.1
dev: true
- /clean-regexp@1.0.0:
- resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==}
- engines: {node: '>=4'}
- dependencies:
- escape-string-regexp: 1.0.5
- dev: true
-
/cli-boxes@2.2.1:
resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==}
engines: {node: '>=6'}
@@ -7240,11 +6997,25 @@ packages:
/color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+ /color-string@1.9.1:
+ resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+ dependencies:
+ color-name: 1.1.4
+ simple-swizzle: 0.2.2
+ dev: false
+
/color-support@1.1.3:
resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==}
hasBin: true
dev: true
+ /color@3.2.1:
+ resolution: {integrity: sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==}
+ dependencies:
+ color-convert: 1.9.3
+ color-string: 1.9.1
+ dev: false
+
/colord@2.9.3:
resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
dev: true
@@ -7263,6 +7034,13 @@ packages:
engines: {node: '>=0.1.90'}
dev: true
+ /colorspace@1.1.4:
+ resolution: {integrity: sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==}
+ dependencies:
+ color: 3.2.1
+ text-hex: 1.0.0
+ dev: false
+
/combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
@@ -7783,11 +7561,6 @@ packages:
'@babel/runtime': 7.22.6
dev: false
- /date-format@4.0.14:
- resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==}
- engines: {node: '>=4.0'}
- dev: false
-
/dayjs@1.11.10:
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
@@ -8320,6 +8093,10 @@ packages:
engines: {node: '>= 4'}
dev: true
+ /enabled@2.0.0:
+ resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
+ dev: false
+
/encode-utf8@1.0.3:
resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==}
dev: false
@@ -8979,35 +8756,6 @@ packages:
- supports-color
dev: true
- /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0):
- resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
- engines: {node: '>=4'}
- peerDependencies:
- '@typescript-eslint/parser': '*'
- eslint: '*'
- eslint-import-resolver-node: '*'
- eslint-import-resolver-typescript: '*'
- eslint-import-resolver-webpack: '*'
- peerDependenciesMeta:
- '@typescript-eslint/parser':
- optional: true
- eslint:
- optional: true
- eslint-import-resolver-node:
- optional: true
- eslint-import-resolver-typescript:
- optional: true
- eslint-import-resolver-webpack:
- optional: true
- dependencies:
- '@typescript-eslint/parser': 5.57.1(eslint@8.53.0)(typescript@5.2.2)
- debug: 3.2.7
- eslint: 8.53.0
- eslint-import-resolver-node: 0.3.9
- transitivePeerDependencies:
- - supports-color
- dev: true
-
/eslint-module-utils@2.8.0(@typescript-eslint/parser@6.10.0)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0):
resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
engines: {node: '>=4'}
@@ -9037,69 +8785,6 @@ packages:
- supports-color
dev: true
- /eslint-plugin-es@4.1.0(eslint@8.53.0):
- resolution: {integrity: sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==}
- engines: {node: '>=8.10.0'}
- peerDependencies:
- eslint: '>=4.19.1'
- dependencies:
- eslint: 8.53.0
- eslint-utils: 2.1.0
- regexpp: 3.2.0
- dev: true
-
- /eslint-plugin-eslint-comments@3.2.0(eslint@8.53.0):
- resolution: {integrity: sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==}
- engines: {node: '>=6.5.0'}
- peerDependencies:
- eslint: '>=4.19.1'
- dependencies:
- escape-string-regexp: 1.0.5
- eslint: 8.53.0
- ignore: 5.2.4
- dev: true
-
- /eslint-plugin-html@7.1.0:
- resolution: {integrity: sha512-fNLRraV/e6j8e3XYOC9xgND4j+U7b1Rq+OygMlLcMg+wI/IpVbF+ubQa3R78EjKB9njT6TQOlcK5rFKBVVtdfg==}
- dependencies:
- htmlparser2: 8.0.2
- dev: true
-
- /eslint-plugin-import@2.29.0(@typescript-eslint/parser@5.57.1)(eslint@8.53.0):
- resolution: {integrity: sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==}
- engines: {node: '>=4'}
- peerDependencies:
- '@typescript-eslint/parser': '*'
- eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
- peerDependenciesMeta:
- '@typescript-eslint/parser':
- optional: true
- dependencies:
- '@typescript-eslint/parser': 5.57.1(eslint@8.53.0)(typescript@5.2.2)
- array-includes: 3.1.7
- array.prototype.findlastindex: 1.2.3
- array.prototype.flat: 1.3.2
- array.prototype.flatmap: 1.3.2
- debug: 3.2.7
- doctrine: 2.1.0
- eslint: 8.53.0
- eslint-import-resolver-node: 0.3.9
- eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.57.1)(eslint-import-resolver-node@0.3.9)(eslint@8.53.0)
- hasown: 2.0.0
- is-core-module: 2.13.1
- is-glob: 4.0.3
- minimatch: 3.1.2
- object.fromentries: 2.0.7
- object.groupby: 1.0.1
- object.values: 1.1.7
- semver: 6.3.1
- tsconfig-paths: 3.14.2
- transitivePeerDependencies:
- - eslint-import-resolver-typescript
- - eslint-import-resolver-webpack
- - supports-color
- dev: true
-
/eslint-plugin-import@2.29.0(@typescript-eslint/parser@6.10.0)(eslint@8.53.0):
resolution: {integrity: sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==}
engines: {node: '>=4'}
@@ -9135,18 +8820,6 @@ packages:
- supports-color
dev: true
- /eslint-plugin-jsonc@2.9.0(eslint@8.53.0):
- resolution: {integrity: sha512-RK+LeONVukbLwT2+t7/OY54NJRccTXh/QbnXzPuTLpFMVZhPuq1C9E07+qWenGx7rrQl0kAalAWl7EmB+RjpGA==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- peerDependencies:
- eslint: '>=6.0.0'
- dependencies:
- '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
- eslint: 8.53.0
- jsonc-eslint-parser: 2.3.0
- natural-compare: 1.4.0
- dev: true
-
/eslint-plugin-jsx-a11y@6.7.1(eslint@8.53.0):
resolution: {integrity: sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==}
engines: {node: '>=4.0'}
@@ -9172,45 +8845,6 @@ packages:
semver: 6.3.1
dev: true
- /eslint-plugin-kuizuo@1.0.1(eslint@8.53.0)(typescript@5.2.2):
- resolution: {integrity: sha512-YPX2tUpDn47mfpu+dRyOBi7kkn9zsQGWzGgKKmSfmLMBSht7tote0mtsvmbrPYNxHDaqDoxr2Y4DwtUV16Z/+Q==}
- dependencies:
- '@typescript-eslint/utils': 5.62.0(eslint@8.53.0)(typescript@5.2.2)
- transitivePeerDependencies:
- - eslint
- - supports-color
- - typescript
- dev: true
-
- /eslint-plugin-markdown@3.0.0(eslint@8.53.0):
- resolution: {integrity: sha512-hRs5RUJGbeHDLfS7ELanT0e29Ocyssf/7kBM+p7KluY5AwngGkDf8Oyu4658/NZSGTTq05FZeWbkxXtbVyHPwg==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- peerDependencies:
- eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
- dependencies:
- eslint: 8.53.0
- mdast-util-from-markdown: 0.8.5
- transitivePeerDependencies:
- - supports-color
- dev: true
-
- /eslint-plugin-n@15.7.0(eslint@8.53.0):
- resolution: {integrity: sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==}
- engines: {node: '>=12.22.0'}
- peerDependencies:
- eslint: '>=7.0.0'
- dependencies:
- builtins: 5.0.1
- eslint: 8.53.0
- eslint-plugin-es: 4.1.0(eslint@8.53.0)
- eslint-utils: 3.0.0(eslint@8.53.0)
- ignore: 5.2.4
- is-core-module: 2.13.1
- minimatch: 3.1.2
- resolve: 1.22.2
- semver: 7.5.4
- dev: true
-
/eslint-plugin-prettier@5.0.1(eslint-config-prettier@9.0.0)(eslint@8.53.0)(prettier@3.0.3):
resolution: {integrity: sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -9232,15 +8866,6 @@ packages:
synckit: 0.8.5
dev: true
- /eslint-plugin-promise@6.1.1(eslint@8.53.0):
- resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- peerDependencies:
- eslint: ^7.0.0 || ^8.0.0
- dependencies:
- eslint: 8.53.0
- dev: true
-
/eslint-plugin-react-hooks@4.6.0(eslint@8.53.0):
resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==}
engines: {node: '>=10'}
@@ -9282,29 +8907,6 @@ packages:
eslint: 8.53.0
dev: true
- /eslint-plugin-unicorn@43.0.2(eslint@8.53.0):
- resolution: {integrity: sha512-DtqZ5mf/GMlfWoz1abIjq5jZfaFuHzGBZYIeuJfEoKKGWRHr2JiJR+ea+BF7Wx2N1PPRoT/2fwgiK1NnmNE3Hg==}
- engines: {node: '>=14.18'}
- peerDependencies:
- eslint: '>=8.18.0'
- dependencies:
- '@babel/helper-validator-identifier': 7.22.5
- ci-info: 3.8.0
- clean-regexp: 1.0.0
- eslint: 8.53.0
- eslint-utils: 3.0.0(eslint@8.53.0)
- esquery: 1.5.0
- indent-string: 4.0.0
- is-builtin-module: 3.2.1
- lodash: 4.17.21
- pluralize: 8.0.0
- read-pkg-up: 7.0.1
- regexp-tree: 0.1.27
- safe-regex: 2.1.1
- semver: 7.5.4
- strip-indent: 3.0.0
- dev: true
-
/eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@6.10.0)(eslint@8.53.0):
resolution: {integrity: sha512-sduiswLJfZHeeBJ+MQaG+xYzSWdRXoSw61DpU13mzWumCkR0ufD0HmO4kdNokjrkluMHpj/7PJeN35pgbhW3kw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -9338,21 +8940,6 @@ packages:
- supports-color
dev: true
- /eslint-plugin-yml@1.8.0(eslint@8.53.0):
- resolution: {integrity: sha512-fgBiJvXD0P2IN7SARDJ2J7mx8t0bLdG6Zcig4ufOqW5hOvSiFxeUyc2g5I1uIm8AExbo26NNYCcTGZT0MXTsyg==}
- engines: {node: ^14.17.0 || >=16.0.0}
- peerDependencies:
- eslint: '>=6.0.0'
- dependencies:
- debug: 4.3.4
- eslint: 8.53.0
- lodash: 4.17.21
- natural-compare: 1.4.0
- yaml-eslint-parser: 1.2.2
- transitivePeerDependencies:
- - supports-color
- dev: true
-
/eslint-rule-composer@0.3.0:
resolution: {integrity: sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==}
engines: {node: '>=4.0.0'}
@@ -9374,33 +8961,6 @@ packages:
estraverse: 5.3.0
dev: true
- /eslint-utils@2.1.0:
- resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==}
- engines: {node: '>=6'}
- dependencies:
- eslint-visitor-keys: 1.3.0
- dev: true
-
- /eslint-utils@3.0.0(eslint@8.53.0):
- resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
- engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
- peerDependencies:
- eslint: '>=5'
- dependencies:
- eslint: 8.53.0
- eslint-visitor-keys: 2.1.0
- dev: true
-
- /eslint-visitor-keys@1.3.0:
- resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==}
- engines: {node: '>=4'}
- dev: true
-
- /eslint-visitor-keys@2.1.0:
- resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
- engines: {node: '>=10'}
- dev: true
-
/eslint-visitor-keys@3.4.3:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -9882,6 +9442,10 @@ packages:
dependencies:
bser: 2.1.1
+ /fecha@4.2.3:
+ resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
+ dev: false
+
/figures@3.2.0:
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
engines: {node: '>=8'}
@@ -9903,6 +9467,12 @@ packages:
flat-cache: 3.1.1
dev: true
+ /file-stream-rotator@0.6.1:
+ resolution: {integrity: sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==}
+ dependencies:
+ moment: 2.29.4
+ dev: false
+
/file-uri-to-path@2.0.0:
resolution: {integrity: sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==}
engines: {node: '>= 6'}
@@ -10003,11 +9573,16 @@ packages:
/flatted@3.2.7:
resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
+ dev: true
/flatted@3.2.9:
resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
dev: true
+ /fn.name@1.1.0:
+ resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==}
+ dev: false
+
/follow-redirects@1.15.2(debug@4.3.4):
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
engines: {node: '>=4.0'}
@@ -10462,10 +10037,6 @@ packages:
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
- /grapheme-splitter@1.0.4:
- resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
- dev: true
-
/graphemer@1.4.0:
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
dev: true
@@ -11015,17 +10586,6 @@ packages:
kind-of: 6.0.3
dev: true
- /is-alphabetical@1.0.4:
- resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==}
- dev: true
-
- /is-alphanumerical@1.0.4:
- resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==}
- dependencies:
- is-alphabetical: 1.0.4
- is-decimal: 1.0.4
- dev: true
-
/is-arguments@1.1.1:
resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
engines: {node: '>= 0.4'}
@@ -11045,6 +10605,10 @@ packages:
/is-arrayish@0.2.1:
resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+ /is-arrayish@0.3.2:
+ resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
+ dev: false
+
/is-bigint@1.0.4:
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
dependencies:
@@ -11111,10 +10675,6 @@ packages:
has-tostringtag: 1.0.0
dev: true
- /is-decimal@1.0.4:
- resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==}
- dev: true
-
/is-descriptor@0.1.6:
resolution: {integrity: sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==}
engines: {node: '>=0.10.0'}
@@ -11182,10 +10742,6 @@ packages:
dependencies:
is-extglob: 2.1.1
- /is-hexadecimal@1.0.4:
- resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==}
- dev: true
-
/is-inside-container@1.0.0:
resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==}
engines: {node: '>=14.16'}
@@ -12565,16 +12121,6 @@ packages:
engines: {node: '>=6'}
hasBin: true
- /jsonc-eslint-parser@2.3.0:
- resolution: {integrity: sha512-9xZPKVYp9DxnM3sd1yAsh/d59iIaswDkai8oTxbursfKYbg/ibjX0IzFt35+VZ8iEW453TVTXztnRvYUQlAfUQ==}
- engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
- dependencies:
- acorn: 8.10.0
- eslint-visitor-keys: 3.4.3
- espree: 9.6.1
- semver: 7.5.4
- dev: true
-
/jsonc-parser@3.1.0:
resolution: {integrity: sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==}
dev: true
@@ -12725,6 +12271,10 @@ packages:
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
dev: true
+ /kuler@2.0.0:
+ resolution: {integrity: sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==}
+ dev: false
+
/language-subtag-registry@0.3.22:
resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==}
dev: true
@@ -13018,17 +12568,16 @@ packages:
is-unicode-supported: 0.1.0
dev: true
- /log4js@6.9.1:
- resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==}
- engines: {node: '>=8.0'}
+ /logform@2.6.0:
+ resolution: {integrity: sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==}
+ engines: {node: '>= 12.0.0'}
dependencies:
- date-format: 4.0.14
- debug: 4.3.4
- flatted: 3.2.7
- rfdc: 1.3.0
- streamroller: 3.1.5
- transitivePeerDependencies:
- - supports-color
+ '@colors/colors': 1.6.0
+ '@types/triple-beam': 1.3.5
+ fecha: 4.2.3
+ ms: 2.1.3
+ safe-stable-stringify: 2.4.3
+ triple-beam: 1.4.1
dev: false
/loglevel-plugin-prefix@0.8.4:
@@ -13231,22 +12780,6 @@ packages:
is-buffer: 1.1.6
dev: false
- /mdast-util-from-markdown@0.8.5:
- resolution: {integrity: sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==}
- dependencies:
- '@types/mdast': 3.0.12
- mdast-util-to-string: 2.0.0
- micromark: 2.11.4
- parse-entities: 2.0.0
- unist-util-stringify-position: 2.0.3
- transitivePeerDependencies:
- - supports-color
- dev: true
-
- /mdast-util-to-string@2.0.0:
- resolution: {integrity: sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==}
- dev: true
-
/mdn-data@2.0.14:
resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
dev: true
@@ -13342,15 +12875,6 @@ packages:
resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
engines: {node: '>= 0.6'}
- /micromark@2.11.4:
- resolution: {integrity: sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==}
- dependencies:
- debug: 4.3.4
- parse-entities: 2.0.0
- transitivePeerDependencies:
- - supports-color
- dev: true
-
/micromatch@3.1.0:
resolution: {integrity: sha512-3StSelAE+hnRvMs8IdVW7Uhk8CVed5tp+kLLGlBP6WiRAXS21GPGu/Nat4WNPXj2Eoc24B02SaeoyozPMfj0/g==}
engines: {node: '>=0.10.0'}
@@ -13921,6 +13445,10 @@ packages:
dependencies:
commander: 11.0.0
+ /moment@2.29.4:
+ resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==}
+ dev: false
+
/morgan@1.10.0:
resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==}
engines: {node: '>= 0.8.0'}
@@ -14033,10 +13561,6 @@ packages:
/nanopop@2.3.0:
resolution: {integrity: sha512-fzN+T2K7/Ah25XU02MJkPZ5q4Tj5FpjmIYq4rvoHX4yb16HzFdCO6JxFFn5Y/oBhQ8no8fUZavnyIv9/+xkBBw==}
- /natural-compare-lite@1.4.0:
- resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
- dev: true
-
/natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
@@ -14216,6 +13740,11 @@ packages:
kind-of: 3.2.2
dev: true
+ /object-hash@2.2.0:
+ resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==}
+ engines: {node: '>= 6'}
+ dev: false
+
/object-hash@3.0.0:
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
engines: {node: '>= 6'}
@@ -14346,6 +13875,12 @@ packages:
dependencies:
wrappy: 1.0.2
+ /one-time@1.0.0:
+ resolution: {integrity: sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==}
+ dependencies:
+ fn.name: 1.1.0
+ dev: false
+
/onetime@5.1.2:
resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
engines: {node: '>=6'}
@@ -14571,17 +14106,6 @@ packages:
callsites: 3.1.0
dev: true
- /parse-entities@2.0.0:
- resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==}
- dependencies:
- character-entities: 1.2.4
- character-entities-legacy: 1.1.4
- character-reference-invalid: 1.1.4
- is-alphanumerical: 1.0.4
- is-decimal: 1.0.4
- is-hexadecimal: 1.0.4
- dev: true
-
/parse-json@5.2.0:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
@@ -15558,11 +15082,6 @@ packages:
safe-regex: 1.1.0
dev: true
- /regexp-tree@0.1.27:
- resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==}
- hasBin: true
- dev: true
-
/regexp.prototype.flags@1.5.0:
resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==}
engines: {node: '>= 0.4'}
@@ -15581,11 +15100,6 @@ packages:
set-function-name: 2.0.1
dev: true
- /regexpp@3.2.0:
- resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
- engines: {node: '>=8'}
- dev: true
-
/regexpu-core@5.3.2:
resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==}
engines: {node: '>=4'}
@@ -15905,12 +15419,6 @@ packages:
ret: 0.1.15
dev: true
- /safe-regex@2.1.1:
- resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==}
- dependencies:
- regexp-tree: 0.1.27
- dev: true
-
/safe-stable-stringify@2.4.3:
resolution: {integrity: sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==}
engines: {node: '>=10'}
@@ -16162,6 +15670,12 @@ packages:
resolution: {integrity: sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==}
engines: {node: '>=14'}
+ /simple-swizzle@0.2.2:
+ resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+ dependencies:
+ is-arrayish: 0.3.2
+ dev: false
+
/sirv@2.0.3:
resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==}
engines: {node: '>= 10'}
@@ -16471,6 +15985,10 @@ packages:
stackframe: 1.3.4
dev: false
+ /stack-trace@0.0.10:
+ resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==}
+ dev: false
+
/stack-utils@2.0.6:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
engines: {node: '>=10'}
@@ -16554,17 +16072,6 @@ packages:
engines: {node: '>=4.0.0'}
dev: false
- /streamroller@3.1.5:
- resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==}
- engines: {node: '>=8.0'}
- dependencies:
- date-format: 4.0.14
- debug: 4.3.4
- fs-extra: 8.1.0
- transitivePeerDependencies:
- - supports-color
- dev: false
-
/strict-uri-encode@1.1.0:
resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==}
engines: {node: '>=0.10.0'}
@@ -17191,6 +16698,10 @@ packages:
engines: {node: '>=8'}
dev: true
+ /text-hex@1.0.0:
+ resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
+ dev: false
+
/text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
dev: true
@@ -17376,6 +16887,11 @@ packages:
engines: {node: '>=12'}
dev: true
+ /triple-beam@1.4.1:
+ resolution: {integrity: sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==}
+ engines: {node: '>= 14.0.0'}
+ dev: false
+
/ts-api-utils@1.0.1(typescript@5.2.2):
resolution: {integrity: sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==}
engines: {node: '>=16.13.0'}
@@ -17517,16 +17033,6 @@ packages:
/tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
- /tsutils@3.21.0(typescript@5.2.2):
- resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
- engines: {node: '>= 6'}
- peerDependencies:
- typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
- dependencies:
- tslib: 1.14.1
- typescript: 5.2.2
- dev: true
-
/turbo-darwin-64@1.10.16:
resolution: {integrity: sha512-+Jk91FNcp9e9NCLYlvDDlp2HwEDp14F9N42IoW3dmHI5ZkGSXzalbhVcrx3DOox3QfiNUHxzWg4d7CnVNCuuMg==}
cpu: [x64]
@@ -17919,12 +17425,6 @@ packages:
set-value: 2.0.1
dev: true
- /unist-util-stringify-position@2.0.3:
- resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==}
- dependencies:
- '@types/unist': 2.0.7
- dev: true
-
/universalify@0.1.2:
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
engines: {node: '>= 4.0.0'}
@@ -18872,6 +18372,45 @@ packages:
execa: 4.1.0
dev: true
+ /winston-daily-rotate-file@4.7.1(winston@3.11.0):
+ resolution: {integrity: sha512-7LGPiYGBPNyGHLn9z33i96zx/bd71pjBn9tqQzO3I4Tayv94WPmBNwKC7CO1wPHdP9uvu+Md/1nr6VSH9h0iaA==}
+ engines: {node: '>=8'}
+ peerDependencies:
+ winston: ^3
+ dependencies:
+ file-stream-rotator: 0.6.1
+ object-hash: 2.2.0
+ triple-beam: 1.4.1
+ winston: 3.11.0
+ winston-transport: 4.6.0
+ dev: false
+
+ /winston-transport@4.6.0:
+ resolution: {integrity: sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg==}
+ engines: {node: '>= 12.0.0'}
+ dependencies:
+ logform: 2.6.0
+ readable-stream: 3.6.2
+ triple-beam: 1.4.1
+ dev: false
+
+ /winston@3.11.0:
+ resolution: {integrity: sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==}
+ engines: {node: '>= 12.0.0'}
+ dependencies:
+ '@colors/colors': 1.6.0
+ '@dabh/diagnostics': 2.0.3
+ async: 3.2.4
+ is-stream: 2.0.1
+ logform: 2.6.0
+ one-time: 1.0.0
+ readable-stream: 3.6.2
+ safe-stable-stringify: 2.4.3
+ stack-trace: 0.0.10
+ triple-beam: 1.4.1
+ winston-transport: 4.6.0
+ dev: false
+
/with@7.0.2:
resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==}
engines: {node: '>= 10.0.0'}
@@ -19057,18 +18596,10 @@ packages:
/yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
- /yaml-eslint-parser@1.2.2:
- resolution: {integrity: sha512-pEwzfsKbTrB8G3xc/sN7aw1v6A6c/pKxLAkjclnAyo5g5qOh6eL9WGu0o3cSDQZKrTNk4KL4lQSwZW+nBkANEg==}
- engines: {node: ^14.17.0 || >=16.0.0}
- dependencies:
- eslint-visitor-keys: 3.4.3
- lodash: 4.17.21
- yaml: 2.3.1
- dev: true
-
/yaml@2.3.1:
resolution: {integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==}
engines: {node: '>= 14'}
+ dev: false
/yargs-parser@18.1.3:
resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}