diff --git a/package.json b/package.json index 5b84432..874abc1 100644 --- a/package.json +++ b/package.json @@ -89,9 +89,9 @@ "ioredis": "^5.3.2", "lodash": "^4.17.21", "mathjs": "^12.4.0", - "minio": "^7.1.3", "mysql2": "^3.9.1", "nanoid": "^3.3.7", + "nestjs-minio": "^2.5.4", "nodemailer": "^6.9.9", "passport": "^0.7.0", "passport-google-oauth20": "^2.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94fdde3..ee995eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -134,15 +134,15 @@ dependencies: mathjs: specifier: ^12.4.0 version: 12.4.0 - minio: - specifier: ^7.1.3 - version: 7.1.3 mysql2: specifier: ^3.9.1 version: 3.9.1 nanoid: specifier: ^3.3.7 version: 3.3.7 + nestjs-minio: + specifier: ^2.5.4 + version: 2.5.4(@nestjs/common@10.3.3)(@nestjs/core@10.3.3) nodemailer: specifier: ^6.9.9 version: 6.9.9 @@ -1733,7 +1733,7 @@ packages: object-assign: 4.1.1 open: 8.4.0 proxy-middleware: 0.15.0 - send: 0.18.0 + send: 1.0.0-beta.2 serve-index: 1.9.1 transitivePeerDependencies: - supports-color @@ -5406,6 +5406,17 @@ packages: dependencies: ms: 2.0.0 + /debug@3.1.0: + resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: true + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -6380,8 +6391,8 @@ packages: resolution: {integrity: sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==} dev: false - /fast-xml-parser@4.3.5: - resolution: {integrity: sha512-sWvP1Pl8H03B8oFJpFR3HE31HUfwtX7Rlf9BNsvdpujD4n7WMhfmu8h9wOV2u+c1k0ZilTADhPqypzx2J690ZQ==} + /fast-xml-parser@4.3.6: + resolution: {integrity: sha512-M2SovcRxD4+vC493Uc2GZVcZaj66CCJhWurC4viynVSTvrpErCShNcDz1lAho6n9REQKvL/ll4A4/fw6Y9z8nw==} hasBin: true dependencies: strnum: 1.0.5 @@ -7572,7 +7583,7 @@ packages: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} dependencies: - which-typed-array: 1.1.14 + which-typed-array: 1.1.15 dev: false /is-unicode-supported@0.1.0: @@ -8818,12 +8829,6 @@ packages: dependencies: mime-db: 1.52.0 - /mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - dev: true - /mime@2.6.0: resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} engines: {node: '>=4.0.0'} @@ -8914,7 +8919,7 @@ packages: block-stream2: 2.1.0 browser-or-node: 2.1.1 buffer-crc32: 0.2.13 - fast-xml-parser: 4.3.5 + fast-xml-parser: 4.3.6 ipaddr.js: 2.1.0 json-stream: 1.0.0 lodash: 4.17.21 @@ -9468,6 +9473,17 @@ packages: /neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + /nestjs-minio@2.5.4(@nestjs/common@10.3.3)(@nestjs/core@10.3.3): + resolution: {integrity: sha512-b99fCEjK1Kt7cNDfANrfhckQiYC10mKoVqfdwUJQaVCWMKTm2E8Kb7oiPIyWyjcVP6RERn5oUMPmf/rZLJEr3A==} + peerDependencies: + '@nestjs/common': '>7.0.0' + '@nestjs/core': '>7.0.0' + dependencies: + '@nestjs/common': 10.3.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.1)(rxjs@7.8.1) + '@nestjs/core': 10.3.3(@nestjs/common@10.3.3)(@nestjs/websockets@10.3.3)(reflect-metadata@0.2.1)(rxjs@7.8.1) + minio: 7.1.3 + dev: false + /next-tick@1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} dev: true @@ -10933,19 +10949,18 @@ packages: dependencies: lru-cache: 6.0.0 - /send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} - engines: {node: '>= 0.8.0'} + /send@1.0.0-beta.2: + resolution: {integrity: sha512-k1yHu/FNK745PULKdsGpQ+bVSXYNwSk+bWnYzbxGZbt5obZc0JKDVANsCRuJD1X/EG15JtP9eZpwxkhUxIYEcg==} + engines: {node: '>= 0.10'} dependencies: - debug: 2.6.9 - depd: 2.0.0 + debug: 3.1.0 destroy: 1.2.0 encodeurl: 1.0.2 escape-html: 1.0.3 etag: 1.8.1 fresh: 0.5.2 http-errors: 2.0.0 - mime: 1.6.0 + mime-types: 2.1.35 ms: 2.1.3 on-finished: 2.4.1 range-parser: 1.2.1 @@ -12202,7 +12217,7 @@ packages: is-arguments: 1.1.1 is-generator-function: 1.0.10 is-typed-array: 1.1.13 - which-typed-array: 1.1.14 + which-typed-array: 1.1.15 dev: false /utility@1.18.0: @@ -12392,8 +12407,8 @@ packages: tr46: 0.0.3 webidl-conversions: 3.0.1 - /which-typed-array@1.1.14: - resolution: {integrity: sha512-VnXFiIW8yNn9kIHN88xvZ4yOWchftKDsRJ8fEPacX/wl1lOvBrhsJ/OeJCXq7B0AaijRuqgzSKalJoPk+D8MPg==} + /which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} engines: {node: '>= 0.4'} dependencies: available-typed-arrays: 1.0.7 diff --git a/src/common/decorators/http.decorator.ts b/src/common/decorators/http.decorator.ts index 22c13f7..f90d311 100644 --- a/src/common/decorators/http.decorator.ts +++ b/src/common/decorators/http.decorator.ts @@ -3,7 +3,15 @@ import type { ExecutionContext } from '@nestjs/common'; import { createParamDecorator } from '@nestjs/common'; import type { FastifyRequest } from 'fastify'; -import { getIp } from '~/utils/ip.util'; +import { getIp, getIsMobile } from '~/utils/ip.util'; + +/** + * 快速获取IP + */ +export const IsMobile = createParamDecorator((_, context: ExecutionContext) => { + const request = context.switchToHttp().getRequest(); + return getIsMobile(request); +}); /** * 快速获取IP diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 2922f8e..25e72a9 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -2,7 +2,7 @@ import { Body, Controller, Headers, Post, UseGuards } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ApiResult } from '~/common/decorators/api-result.decorator'; -import { Ip } from '~/common/decorators/http.decorator'; +import { Ip, IsMobile } from '~/common/decorators/http.decorator'; import { UserService } from '../user/user.service'; @@ -32,9 +32,12 @@ export class AuthController { async login( @Body() dto: LoginDto, @Ip() ip: string, + @IsMobile() isMobile: boolean, @Headers('user-agent') ua: string ): Promise { - await this.captchaService.checkImgCaptcha(dto.captchaId, dto.verifyCode); + if(!isMobile){ + await this.captchaService.checkImgCaptcha(dto.captchaId, dto.verifyCode); + } const token = await this.authService.login(dto.username, dto.password, ip, ua); return { token }; } @@ -43,6 +46,11 @@ export class AuthController { @ApiSecurityAuth() @ApiOperation({ summary: '屏幕解锁,使用密码和token' }) @ApiResult({ type: LoginToken }) + + + + + async unlock(@Body() dto: LoginDto, @AuthUser() user: IAuthUser): Promise { await this.authService.unlock(user.uid, dto.password); return true; diff --git a/src/modules/auth/dto/auth.dto.ts b/src/modules/auth/dto/auth.dto.ts index 436ed04..0919641 100644 --- a/src/modules/auth/dto/auth.dto.ts +++ b/src/modules/auth/dto/auth.dto.ts @@ -13,7 +13,7 @@ export class LoginDto { @MinLength(6) password: string; - @ApiProperty({ description: '验证码标识' }) + @ApiProperty({ description: '验证码标识,手机端不需要' }) @IsOptional() captchaId: string; diff --git a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts index 0d0fec2..8244b59 100644 --- a/src/modules/materials_inventory/in_out/materials_in_out.dto.ts +++ b/src/modules/materials_inventory/in_out/materials_in_out.dto.ts @@ -55,10 +55,12 @@ export class MaterialsInOutDto { quantity: number; @ApiProperty({ description: '单价' }) + @IsOptional() @IsNumber() unitPrice: number; @ApiProperty({ description: '金额' }) + @IsOptional() @IsNumber() amount: number; diff --git a/src/modules/netdisk/manager/manage.service.ts b/src/modules/netdisk/manager/manage.service.ts index b6d0c74..d1a1209 100644 --- a/src/modules/netdisk/manager/manage.service.ts +++ b/src/modules/netdisk/manager/manage.service.ts @@ -1,41 +1,22 @@ -import { basename, extname } from 'node:path'; - import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { isEmpty } from 'lodash'; - import { ConfigKeyPaths } from '~/config'; -import { - NETDISK_COPY_SUFFIX, - NETDISK_DELIMITER, - NETDISK_HANDLE_MAX_ITEM, - NETDISK_LIMIT -} from '~/constants/oss.constant'; - -import { AccountInfo } from '~/modules/user/user.model'; -import { UserService } from '~/modules/user/user.service'; - -import { fileRename, generateRandomValue } from '~/utils'; - +import { generateRandomValue } from '~/utils'; import { SFileInfo, SFileInfoDetail, SFileList } from './manage.class'; -import { FileOpItem } from './manage.dto'; import * as Minio from 'Minio'; +import { InjectMinio } from 'nestjs-minio'; +import { Client } from 'minio'; + @Injectable() export class NetDiskManageService { private get ossConfig() { return this.configService.get('oss', { infer: true }); } - private minioClient: Minio.Client; - constructor(private configService: ConfigService) { - this.minioClient = new Minio.Client({ - endPoint: this.ossConfig.domain, - port: this.ossConfig.port, - useSSL: this.ossConfig.useSSL, - accessKey: this.ossConfig.accessKey, - secretKey: this.ossConfig.secretKey - }); - } - + constructor( + private configService: ConfigService, + @InjectMinio() private readonly minioClient: Client + ) {} /** * 获取文件列表 * @param prefix 当前文件夹路径,搜索模式下会被忽略 diff --git a/src/modules/netdisk/netdisk.module.ts b/src/modules/netdisk/netdisk.module.ts index 0ea272f..cc067e2 100644 --- a/src/modules/netdisk/netdisk.module.ts +++ b/src/modules/netdisk/netdisk.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; - +import { ConfigService } from '@nestjs/config'; import { RouterModule } from '@nestjs/core'; import { UserModule } from '../user/user.module'; @@ -9,6 +9,22 @@ import { NetDiskOverviewController } from './overview/overview.controller'; import { NetDiskOverviewService } from './overview/overview.service'; import { MinioService } from './minio/minio.service'; import { NetDiskManageService } from './manager/manage.service'; +import { NestMinioModule } from 'nestjs-minio'; +// const getMinioConfig = () => { +// const configService = new ConfigService(); +// const endPoint = configService.get('MINIO_ENDPOINT', 'localhost'); +// const accessKey = configService.get('MINIO_ACCESSKEY', 'accessKey'); +// const secretKey = configService.get('MINIO_SECRET_KEY', 'secretKey'); + +// return NestMinioModule.register({ +// isGlobal: true, +// endPoint, +// port: 9000, +// accessKey, +// secretKey, +// useSSL: false +// }); +// }; @Module({ imports: [ @@ -18,9 +34,56 @@ import { NetDiskManageService } from './manager/manage.service'; path: 'netdisk', module: NetdiskModule } - ]) + ]), + // getMinioConfig() + NestMinioModule.registerAsync({ + inject: [ConfigService], + isGlobal: true, + useFactory: async (configService: ConfigService) => { + const ossConfig = configService.get('oss'); + return { + endPoint: ossConfig.domain, + port: ossConfig.port, + useSSL: ossConfig.useSSL, + accessKey: ossConfig.accessKey, + secretKey: ossConfig.secretKey + }; + } + // endPoint: 'play.min.io', + // port: 9000, + // useSSL: true, + // accessKey: 'Q3AM3UQ867SPQQA43P2F', + // secretKey: 'zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG' + }) ], controllers: [NetDiskManageController, NetDiskOverviewController], providers: [NetDiskManageService, NetDiskOverviewService, MinioService] }) export class NetdiskModule {} +// TypeOrmModule.forRootAsync({ +// inject: [ConfigService], +// useFactory: (configService: ConfigService) => { +// let loggerOptions: LoggerOptions = env('DB_LOGGING') as 'all'; + +// try { +// // 解析成 js 数组 ['error'] +// loggerOptions = JSON.parse(loggerOptions); +// } catch { +// // ignore +// } + +// return { +// ...configService.get('database'), +// autoLoadEntities: true, +// logging: loggerOptions, +// logger: new TypeORMLogger(loggerOptions) +// }; +// }, +// // dataSource receives the configured DataSourceOptions +// // and returns a Promise. +// dataSourceFactory: async options => { +// const dataSource = await new DataSource(options).initialize(); +// return dataSource; +// } +// }) +// ], diff --git a/src/utils/ip.util.ts b/src/utils/ip.util.ts index b9079ca..58682ec 100644 --- a/src/utils/ip.util.ts +++ b/src/utils/ip.util.ts @@ -25,6 +25,11 @@ function isLAN(ip: string) { ); } +// 是否来自手机的请求 +export function getIsMobile(request: FastifyRequest | IncomingMessage): boolean { + return request.headers['user-agent'].includes('Dart'); +} + export function getIp(request: FastifyRequest | IncomingMessage) { const req = request as any;