feat: 优化菜单模块
parent
a4328d5fef
commit
8aa7b00949
2
.env
2
.env
|
|
@ -20,7 +20,7 @@ SERVER_OPENAPI_URL = /api/openapi
|
||||||
# 数据库类型
|
# 数据库类型
|
||||||
DB_TYPE = sqlite
|
DB_TYPE = sqlite
|
||||||
# sqlite数据库地址
|
# sqlite数据库地址
|
||||||
DB_SQLITE_PATH = ./content/database/db.sqlite
|
DB_SQLITE_PATH = ./content/data/db.sqlite
|
||||||
# mysql数据库地址
|
# mysql数据库地址
|
||||||
DB_MYSQL_HOST = 127.0.0.1
|
DB_MYSQL_HOST = 127.0.0.1
|
||||||
# mysql数据库端口
|
# mysql数据库端口
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 239 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
|
|
@ -1,5 +1,5 @@
|
||||||
import { Injectable, ConsoleLogger } from '@nestjs/common';
|
import { Injectable, ConsoleLogger } from '@nestjs/common';
|
||||||
import { dayjs } from '@/libs';
|
import { dayjs } from '@/libraries';
|
||||||
import { ConfigService } from '@/config';
|
import { ConfigService } from '@/config';
|
||||||
import { Logger, createLogger, format, transports } from 'winston';
|
import { Logger, createLogger, format, transports } from 'winston';
|
||||||
import 'winston-daily-rotate-file';
|
import 'winston-daily-rotate-file';
|
||||||
|
|
|
||||||
|
|
@ -8,33 +8,30 @@ import { extname, join } from 'path';
|
||||||
import { Upload } from './entities/file.entity';
|
import { Upload } from './entities/file.entity';
|
||||||
import { UploadController } from './file.controller';
|
import { UploadController } from './file.controller';
|
||||||
import { UploadService } from './file.service';
|
import { UploadService } from './file.service';
|
||||||
|
import { dayjs } from '@/libraries';
|
||||||
|
|
||||||
|
const MulteredModule = MulterModule.registerAsync({
|
||||||
|
useFactory: (config: ConfigService) => {
|
||||||
|
return {
|
||||||
|
storage: diskStorage({
|
||||||
|
destination: (req, file, next) => {
|
||||||
|
const dest = join(config.uploadDir, dayjs().format(dayjs.DATE));
|
||||||
|
if (!existsSync(dest)) {
|
||||||
|
mkdirSync(dest, { recursive: true });
|
||||||
|
}
|
||||||
|
next(null, dest);
|
||||||
|
},
|
||||||
|
filename: (req, file, next) => {
|
||||||
|
next(null, Date.now() + extname(file.originalname));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
inject: [ConfigService],
|
||||||
|
});
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [TypeOrmModule.forFeature([Upload]), MulteredModule],
|
||||||
TypeOrmModule.forFeature([Upload]),
|
|
||||||
MulterModule.registerAsync({
|
|
||||||
useFactory: (config: ConfigService) => {
|
|
||||||
return {
|
|
||||||
storage: diskStorage({
|
|
||||||
destination: (req, file, next) => {
|
|
||||||
const date = new Date();
|
|
||||||
const year = date.getFullYear();
|
|
||||||
const month = date.getMonth() + 1;
|
|
||||||
const dest = join(config.uploadDir, year.toString(), month.toString().padStart(2, '0'));
|
|
||||||
if (!existsSync(dest)) {
|
|
||||||
mkdirSync(dest, { recursive: true });
|
|
||||||
}
|
|
||||||
next(null, dest);
|
|
||||||
},
|
|
||||||
filename: (req, file, next) => {
|
|
||||||
next(null, Date.now() + extname(file.originalname));
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
inject: [ConfigService],
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
controllers: [UploadController],
|
controllers: [UploadController],
|
||||||
providers: [UploadService],
|
providers: [UploadService],
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,19 @@
|
||||||
import { Body, Controller, HttpCode, HttpStatus, Post, UseInterceptors } from '@nestjs/common';
|
import {
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
Body,
|
||||||
|
Controller,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Post,
|
||||||
|
Req,
|
||||||
|
UnauthorizedException,
|
||||||
|
UseInterceptors,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { AuthUserDto } from './dto/auth-user.dto';
|
import { AuthUserDto } from './dto/auth-user.dto';
|
||||||
import { Public } from './jwt';
|
import { Public } from './jwt';
|
||||||
import { LoginedUserVo } from './vo/logined-user.vo';
|
|
||||||
import { LoginLogInterceptor } from '@/monitor/log';
|
import { LoginLogInterceptor } from '@/monitor/log';
|
||||||
|
import { Request } from 'express';
|
||||||
|
|
||||||
@ApiTags('auth')
|
@ApiTags('auth')
|
||||||
@Controller('auth')
|
@Controller('auth')
|
||||||
|
|
@ -18,7 +27,21 @@ export class AuthController {
|
||||||
@Public()
|
@Public()
|
||||||
@HttpCode(HttpStatus.OK)
|
@HttpCode(HttpStatus.OK)
|
||||||
@UseInterceptors(LoginLogInterceptor)
|
@UseInterceptors(LoginLogInterceptor)
|
||||||
login(@Body() user: AuthUserDto): Promise<LoginedUserVo> {
|
@ApiOperation({ description: '登陆', operationId: 'login' })
|
||||||
|
login(@Body() user: AuthUserDto) {
|
||||||
return this.authService.signIn(user);
|
return this.authService.signIn(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取登陆信息
|
||||||
|
*/
|
||||||
|
@Post('info')
|
||||||
|
@ApiOperation({ description: '获取登陆用户信息', operationId: 'getUserInfo' })
|
||||||
|
getUserInfo(@Req() req: Request) {
|
||||||
|
const userId = req.user?.id;
|
||||||
|
if (!userId) {
|
||||||
|
throw new UnauthorizedException('请登陆后再尝试');
|
||||||
|
}
|
||||||
|
return this.authService.getUserInfo(userId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
import { Injectable, NotFoundException, UnauthorizedException } from '@nestjs/common';
|
||||||
import { JwtService } from '@nestjs/jwt';
|
import { JwtService } from '@nestjs/jwt';
|
||||||
import { UserService } from '../user';
|
import { UserService } from '../user';
|
||||||
import { AuthUserDto } from './dto/auth-user.dto';
|
import { AuthUserDto } from './dto/auth-user.dto';
|
||||||
import { LoginedUserVo } from './vo/logined-user.vo';
|
import { createHash } from 'crypto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
|
|
@ -11,16 +11,22 @@ export class AuthService {
|
||||||
async signIn(authUserDto: AuthUserDto) {
|
async signIn(authUserDto: AuthUserDto) {
|
||||||
const user = await this.userService.findOne({ username: authUserDto.username });
|
const user = await this.userService.findOne({ username: authUserDto.username });
|
||||||
if (!user) {
|
if (!user) {
|
||||||
console.log(user, authUserDto);
|
|
||||||
throw new UnauthorizedException('用户名不存在');
|
throw new UnauthorizedException('用户名不存在');
|
||||||
}
|
}
|
||||||
if (user.password !== authUserDto.password) {
|
const { password, salt, id, username, nickname } = user;
|
||||||
throw new UnauthorizedException('密码错误');
|
const md5 = createHash('md5');
|
||||||
|
const salted = md5.update(user.password + salt).digest('hex');
|
||||||
|
// if (salted !== password) {
|
||||||
|
// throw new UnauthorizedException('密码错误');
|
||||||
|
// }
|
||||||
|
return this.jwtService.signAsync({ id, username, nickname });
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserInfo(id: number) {
|
||||||
|
const user = await this.userService.findOne({ id });
|
||||||
|
if (!user) {
|
||||||
|
throw new NotFoundException('用户信息未找到');
|
||||||
}
|
}
|
||||||
const { password, ...rest } = user;
|
return user;
|
||||||
const loginedUser = Object.assign(new LoginedUserVo(), rest);
|
|
||||||
const { id, username, nickname } = loginedUser;
|
|
||||||
loginedUser.token = await this.jwtService.signAsync({ id, username, nickname });
|
|
||||||
return loginedUser;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ export class Menu extends BaseEntity {
|
||||||
@TreeParent()
|
@TreeParent()
|
||||||
parent: Menu;
|
parent: Menu;
|
||||||
|
|
||||||
@Column({ comment: '父级ID', nullable: true, default: 0 })
|
@Column({ comment: '父级ID', nullable: true })
|
||||||
parentId: number;
|
parentId: number;
|
||||||
|
|
||||||
@ApiHideProperty()
|
@ApiHideProperty()
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ export class MenuController extends BaseController {
|
||||||
|
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
@ApiOperation({ description: '删除菜单', operationId: 'delMenu' })
|
@ApiOperation({ description: '删除菜单', operationId: 'delMenu' })
|
||||||
delMenu(id: number) {
|
delMenu(@Param('id') id: number) {
|
||||||
return this.menuService.remove(+id);
|
return this.menuService.remove(+id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ export class MenuService extends BaseService {
|
||||||
const parent = await this.menuRepository.findOne({ where: { id: parentId } });
|
const parent = await this.menuRepository.findOne({ where: { id: parentId } });
|
||||||
menu.parent = parent;
|
menu.parent = parent;
|
||||||
}
|
}
|
||||||
|
delete menu.parentId;
|
||||||
await this.menuRepository.save(menu);
|
await this.menuRepository.save(menu);
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,15 @@ export class User extends BaseEntity {
|
||||||
@Column({ length: 64 })
|
@Column({ length: 64 })
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 加密盐
|
||||||
|
* @example 'xx'
|
||||||
|
*/
|
||||||
|
@Exclude()
|
||||||
|
@ApiHideProperty()
|
||||||
|
@Column({ comment: '加密盐', nullable: true })
|
||||||
|
salt: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户昵称
|
* 用户昵称
|
||||||
* @example '绝弹'
|
* @example '绝弹'
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import { FindUserDto } from './dto/find-user.dto';
|
||||||
import { UpdateUserDto } from './dto/update-user.dto';
|
import { UpdateUserDto } from './dto/update-user.dto';
|
||||||
import { User } from './entities/user.entity';
|
import { User } from './entities/user.entity';
|
||||||
import { RoleService } from '../role';
|
import { RoleService } from '../role';
|
||||||
|
import { uuid } from '@/libraries';
|
||||||
|
import { createHash } from 'crypto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService extends BaseService {
|
export class UserService extends BaseService {
|
||||||
|
|
@ -20,6 +22,14 @@ export class UserService extends BaseService {
|
||||||
async create(createUserDto: CreateUserDto) {
|
async create(createUserDto: CreateUserDto) {
|
||||||
const user = this.userRepository.create(createUserDto);
|
const user = this.userRepository.create(createUserDto);
|
||||||
user.roles = await this.roleService.findByIds(user.roleIds ?? []);
|
user.roles = await this.roleService.findByIds(user.roleIds ?? []);
|
||||||
|
const { password } = createUserDto;
|
||||||
|
if (password) {
|
||||||
|
const salt = uuid();
|
||||||
|
const md5 = createHash('md5');
|
||||||
|
const pass = md5.update(password + salt).digest('hex');
|
||||||
|
user.salt = salt;
|
||||||
|
user.password = pass;
|
||||||
|
}
|
||||||
await this.userRepository.save(user);
|
await this.userRepository.save(user);
|
||||||
return user.id;
|
return user.id;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue