feat(25-06-03): 修改重置密码
This commit is contained in:
parent
ab7badc4ae
commit
5407e27b13
147
star-tune/cursor.md
Normal file
147
star-tune/cursor.md
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
# 本项目使用cursou说明
|
||||||
|
|
||||||
|
## 服务注入
|
||||||
|
|
||||||
|
> 大部分服务已经全局注入,直接在service中使用即可
|
||||||
|
|
||||||
|
- 日志服务:private readonly logger: CustomLogger,
|
||||||
|
- redis服务:private readonly redis: RedisService,
|
||||||
|
- 配置服务:private readonly config: ConfigService,
|
||||||
|
- 工具服务:private readonly utils: UtilsService,
|
||||||
|
- 数据库服务:private readonly database: DatabaseService,
|
||||||
|
|
||||||
|
以上服务均已在appModule全局注入,不需要再向module中注入
|
||||||
|
|
||||||
|
## 路径别名
|
||||||
|
|
||||||
|
@代指src目录,请注意,引入任何文件采用路径别名,除开当前目录下的文件,不允许使用../获取上级目录
|
||||||
|
|
||||||
|
## 数据库
|
||||||
|
|
||||||
|
1. 数据库实体在@/drizzle/schema,通过这样导入:
|
||||||
|
```ts
|
||||||
|
import {
|
||||||
|
user,
|
||||||
|
userPassword,
|
||||||
|
userProfile,
|
||||||
|
userSignatureHistory,
|
||||||
|
} from '@/drizzle/schema';
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**操作数据严格按照数据库实体的定义
|
||||||
|
|
||||||
|
2. 数据库使用drizzle和mysql2,下面是使用示例
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { user } from '@/drizzle/schema';
|
||||||
|
// dto为参数
|
||||||
|
const userExists = await this.database.db
|
||||||
|
.select({
|
||||||
|
userId: user.userId,
|
||||||
|
})
|
||||||
|
.from(user)
|
||||||
|
.where(
|
||||||
|
or(
|
||||||
|
eq(user.email, dto.email),
|
||||||
|
eq(user.username, dto.username),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.execute();
|
||||||
|
if (userExists.length > 0) {
|
||||||
|
throw new BadRequestException('用户已存在');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注释
|
||||||
|
|
||||||
|
**非常重要** !!!!!!!!!!!
|
||||||
|
1. 编写代码已经要有非常详细的注释,最好每行都有注释
|
||||||
|
|
||||||
|
参考
|
||||||
|
```ts
|
||||||
|
// 检查邮箱是否可用
|
||||||
|
async checkEmail(dto: CheckEmailDto) {
|
||||||
|
const exists = await this.database.db
|
||||||
|
.select()
|
||||||
|
.from(user)
|
||||||
|
.where(and(eq(user.email, dto.email), eq(user.isDeleted, 0)))
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return { available: exists.length === 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送验证码
|
||||||
|
async sendEmailCode(dto: SendEmailCodeDto) {
|
||||||
|
// 邮箱验证码key
|
||||||
|
const codeKey = `${EmailCodeType[dto.type]}:${dto.email}`;
|
||||||
|
// 判断Key是否存在
|
||||||
|
const keyExists = await this.redis.exists(codeKey);
|
||||||
|
// 验证码过期时间
|
||||||
|
const codeEX = this.config.get<number>('email.codeEX') || 300;
|
||||||
|
// 验证码冷却时间
|
||||||
|
const codeEP = this.config.get<number>('email.codeEP') || 60;
|
||||||
|
// 判断是否存在验证码
|
||||||
|
if (keyExists) {
|
||||||
|
// 获取Key的过期时间
|
||||||
|
const ttl = await this.redis.ttl(codeKey);
|
||||||
|
if (ttl > codeEX - codeEP) {
|
||||||
|
// 再等等吧
|
||||||
|
throw new BadRequestException(`请等待${ttl}秒后再试`);
|
||||||
|
} else {
|
||||||
|
// 续杯
|
||||||
|
await this.redis.expire(codeKey, codeEX);
|
||||||
|
// 获取验证码
|
||||||
|
const code = await this.redis.get(codeKey);
|
||||||
|
// todo重新发送验证码
|
||||||
|
this.logger.debug(`重新发送验证码: ${code}`);
|
||||||
|
await this.utils.sendEmail({
|
||||||
|
to: dto.email,
|
||||||
|
subject: '账户注册验证码',
|
||||||
|
text: `您的验证码是:${code},5分钟内有效`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 生成验证码
|
||||||
|
const code = Math.random().toString().slice(2, 8);
|
||||||
|
// todo发送验证码
|
||||||
|
this.logger.debug(`发送验证码: ${code}`);
|
||||||
|
await this.utils.sendEmail({
|
||||||
|
to: dto.email,
|
||||||
|
subject: '账户注册验证码',
|
||||||
|
text: `您的验证码是:${code},5分钟内有效`,
|
||||||
|
});
|
||||||
|
// 存储验证码
|
||||||
|
await this.redis.set(codeKey, code, codeEX);
|
||||||
|
}
|
||||||
|
return { message: '验证码已发送' };
|
||||||
|
}
|
||||||
|
```
|
||||||
|
2. 每个方法前都要携带写作者、写作时间、方法描述,写作者为`Nie`
|
||||||
|
|
||||||
|
## 常量
|
||||||
|
|
||||||
|
常量这样导入 `import { EmailCodeType } from '@/type/enum';`
|
||||||
|
|
||||||
|
常量用于一些系统的关键字
|
||||||
|
|
||||||
|
|
||||||
|
## redis
|
||||||
|
|
||||||
|
使用参考
|
||||||
|
```ts
|
||||||
|
// 加锁
|
||||||
|
const lock = await this.redis.lock(
|
||||||
|
`${EmailCodeType.REGISTER}:${dto.email}`,
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
方法一般在RedisService中定义
|
||||||
|
|
||||||
|
## 文件命名
|
||||||
|
|
||||||
|
采用小驼峰加`.service`、`.dto`类似的方式命名,禁止使用中划线
|
||||||
|
|
||||||
|
错误示例
|
||||||
|
`add-user.dto.ts`
|
||||||
|
正确示例
|
||||||
|
`addUser.dto.ts
|
@ -1,10 +1,10 @@
|
|||||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||||
|
import { UserGuard } from '@/type/userGuard';
|
||||||
|
|
||||||
export const User = createParamDecorator(
|
export const User = createParamDecorator(
|
||||||
(data: string, ctx: ExecutionContext) => {
|
(data: keyof UserGuard | undefined, ctx: ExecutionContext) => {
|
||||||
const request = ctx.switchToHttp().getRequest();
|
const request = ctx.switchToHttp().getRequest<{ user: UserGuard }>();
|
||||||
const user = request.user;
|
const user = request.user;
|
||||||
|
|
||||||
return data ? user?.[data] : user;
|
return data ? user?.[data] : user;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { UtilsService } from '@/common/utils/utils.service';
|
import { UtilsService } from '@/common/utils/utils.service';
|
||||||
import { UserGuard } from '@/type/userGuard';
|
import { UserGuard, UserGuardType } from '@/type/userGuard';
|
||||||
import { FastifyRequest } from 'fastify';
|
import { FastifyRequest } from 'fastify';
|
||||||
|
|
||||||
// 扩展 FastifyRequest 类型以包含 user 属性
|
// 扩展 FastifyRequest 类型以包含 user 属性
|
||||||
@ -26,15 +26,14 @@ export class AuthGuard implements CanActivate {
|
|||||||
canActivate(context: ExecutionContext): boolean {
|
canActivate(context: ExecutionContext): boolean {
|
||||||
const request = context.switchToHttp().getRequest<FastifyRequest>();
|
const request = context.switchToHttp().getRequest<FastifyRequest>();
|
||||||
const path = request.url;
|
const path = request.url;
|
||||||
console.log(path);
|
// console.log(path);
|
||||||
|
|
||||||
// 检查白名单路由
|
// 检查白名单路由
|
||||||
const whitelist = this.config.get<string[]>('jwt.whitelist') || [];
|
const whitelist = this.config.get<string[]>('jwt.whitelist') || [];
|
||||||
console.log(whitelist);
|
// console.log(whitelist);
|
||||||
if (whitelist.some((route) => path.startsWith(route))) {
|
if (whitelist.some((route) => path.startsWith(route))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
console.log('?', request.headers);
|
|
||||||
|
|
||||||
// 获取并验证 token
|
// 获取并验证 token
|
||||||
const authHeader = request.headers.authorization;
|
const authHeader = request.headers.authorization;
|
||||||
@ -48,7 +47,20 @@ export class AuthGuard implements CanActivate {
|
|||||||
try {
|
try {
|
||||||
userGuard = this.utils.verifyToken(token);
|
userGuard = this.utils.verifyToken(token);
|
||||||
} catch {
|
} catch {
|
||||||
throw new UnauthorizedException('无效的访问令牌');
|
throw new UnauthorizedException('无效的访问令牌1');
|
||||||
|
}
|
||||||
|
if (this.config.get<string>('env') === 'production') {
|
||||||
|
if (userGuard.type === UserGuardType.REFRESH) {
|
||||||
|
if (path === '/api/user/refreshToken') {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw new UnauthorizedException('无效的访问令牌2');
|
||||||
|
}
|
||||||
|
} else if (userGuard.type === UserGuardType.ACCESS) {
|
||||||
|
if (path === '/api/user/refreshToken') {
|
||||||
|
throw new UnauthorizedException('无效的访问令牌3');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将用户信息附加到请求对象
|
// 将用户信息附加到请求对象
|
||||||
|
@ -70,6 +70,7 @@ async function bootstrap() {
|
|||||||
SwaggerModule.setup('api/docs', app, document);
|
SwaggerModule.setup('api/docs', app, document);
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`Swagger 配置完成: http://${host === '0.0.0.0' ? '127.0.0.1' : host}:${port}/api/docs`,
|
`Swagger 配置完成: http://${host === '0.0.0.0' ? '127.0.0.1' : host}:${port}/api/docs`,
|
||||||
|
'Swagger',
|
||||||
);
|
);
|
||||||
|
|
||||||
// 测试不同级别的日志输出
|
// 测试不同级别的日志输出
|
||||||
|
14
star-tune/src/module/user/dto/change-password.dto.ts
Normal file
14
star-tune/src/module/user/dto/change-password.dto.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsString, MinLength } from 'class-validator';
|
||||||
|
|
||||||
|
export class ChangePasswordDto {
|
||||||
|
@ApiProperty({ description: '旧密码' })
|
||||||
|
@IsString()
|
||||||
|
@MinLength(6)
|
||||||
|
oldPassword: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '新密码' })
|
||||||
|
@IsString()
|
||||||
|
@MinLength(6)
|
||||||
|
newPassword: string;
|
||||||
|
}
|
17
star-tune/src/module/user/dto/reset-password.dto.ts
Normal file
17
star-tune/src/module/user/dto/reset-password.dto.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsString, MinLength, IsEmail } from 'class-validator';
|
||||||
|
|
||||||
|
export class ResetPasswordDto {
|
||||||
|
@ApiProperty({ description: '邮箱' })
|
||||||
|
@IsEmail()
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '验证码' })
|
||||||
|
@IsString()
|
||||||
|
code: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '新密码' })
|
||||||
|
@IsString()
|
||||||
|
@MinLength(6)
|
||||||
|
newPassword: string;
|
||||||
|
}
|
@ -18,6 +18,8 @@ import { EmailLoginDto } from './dto/email-login.dto';
|
|||||||
import { UpdateUserDto } from './dto/update-user.dto';
|
import { UpdateUserDto } from './dto/update-user.dto';
|
||||||
import { UpdateProfileDto } from './dto/update-profile.dto';
|
import { UpdateProfileDto } from './dto/update-profile.dto';
|
||||||
import { UpdateSignatureDto } from './dto/update-signature.dto';
|
import { UpdateSignatureDto } from './dto/update-signature.dto';
|
||||||
|
import { ChangePasswordDto } from './dto/change-password.dto';
|
||||||
|
import { ResetPasswordDto } from './dto/reset-password.dto';
|
||||||
import { AuthGuard } from '../../guards/auth.guard';
|
import { AuthGuard } from '../../guards/auth.guard';
|
||||||
import { User } from '../../decorators/user.decorator';
|
import { User } from '../../decorators/user.decorator';
|
||||||
import {
|
import {
|
||||||
@ -26,6 +28,7 @@ import {
|
|||||||
ApiBearerAuth,
|
ApiBearerAuth,
|
||||||
ApiResponse,
|
ApiResponse,
|
||||||
} from '@nestjs/swagger';
|
} from '@nestjs/swagger';
|
||||||
|
import { UserGuard } from '@/type/userGuard';
|
||||||
|
|
||||||
@ApiTags('用户管理')
|
@ApiTags('用户管理')
|
||||||
@ApiBearerAuth('access-token')
|
@ApiBearerAuth('access-token')
|
||||||
@ -62,7 +65,7 @@ export class UserController {
|
|||||||
|
|
||||||
@ApiOperation({ summary: '检查邮箱是否可用' })
|
@ApiOperation({ summary: '检查邮箱是否可用' })
|
||||||
@ApiResponse({ status: 200, description: '邮箱检查结果' })
|
@ApiResponse({ status: 200, description: '邮箱检查结果' })
|
||||||
@Post('check-email')
|
@Post('checkEmail')
|
||||||
checkEmail(@Body() dto: CheckEmailDto) {
|
checkEmail(@Body() dto: CheckEmailDto) {
|
||||||
return this.userService.checkEmail(dto);
|
return this.userService.checkEmail(dto);
|
||||||
}
|
}
|
||||||
@ -73,7 +76,7 @@ export class UserController {
|
|||||||
status: 400,
|
status: 400,
|
||||||
description: '发送失败,可能是因为发送过于频繁',
|
description: '发送失败,可能是因为发送过于频繁',
|
||||||
})
|
})
|
||||||
@Post('send-code')
|
@Post('sendCode')
|
||||||
sendEmailCode(@Body() dto: SendEmailCodeDto) {
|
sendEmailCode(@Body() dto: SendEmailCodeDto) {
|
||||||
return this.userService.sendEmailCode(dto);
|
return this.userService.sendEmailCode(dto);
|
||||||
}
|
}
|
||||||
@ -101,13 +104,11 @@ export class UserController {
|
|||||||
// return this.userService.emailLogin(dto);
|
// return this.userService.emailLogin(dto);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// @Post('refresh-token')
|
@UseGuards(AuthGuard)
|
||||||
// refreshToken(@Headers('refresh-token') refreshToken: string) {
|
@Post('refreshToken')
|
||||||
// if (!refreshToken) {
|
refreshToken(@User() userGuard: UserGuard) {
|
||||||
// throw new UnauthorizedException('缺少刷新令牌');
|
return this.userService.refreshToken(userGuard);
|
||||||
// }
|
}
|
||||||
// return this.userService.refreshToken(refreshToken);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @Post('logout')
|
// @Post('logout')
|
||||||
// @UseGuards(AuthGuard)
|
// @UseGuards(AuthGuard)
|
||||||
@ -156,4 +157,25 @@ export class UserController {
|
|||||||
// deleteUser(@User('userId') userId: number) {
|
// deleteUser(@User('userId') userId: number) {
|
||||||
// return this.userService.deleteUser(userId);
|
// return this.userService.deleteUser(userId);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@ApiOperation({ summary: '修改密码' })
|
||||||
|
@ApiResponse({ status: 200, description: '密码修改成功' })
|
||||||
|
@ApiResponse({ status: 400, description: '旧密码错误' })
|
||||||
|
@ApiBearerAuth('access-token')
|
||||||
|
@Patch('password')
|
||||||
|
@UseGuards(AuthGuard)
|
||||||
|
changePassword(
|
||||||
|
@User('userId') userId: number,
|
||||||
|
@Body() dto: ChangePasswordDto,
|
||||||
|
) {
|
||||||
|
return this.userService.changePassword(userId, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation({ summary: '重置密码(忘记密码)' })
|
||||||
|
@ApiResponse({ status: 200, description: '密码重置成功' })
|
||||||
|
@ApiResponse({ status: 400, description: '验证码错误或已过期' })
|
||||||
|
@Post('resetPassword')
|
||||||
|
resetPassword(@Body() dto: ResetPasswordDto) {
|
||||||
|
return this.userService.resetPassword(dto);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,15 @@ import {
|
|||||||
Injectable,
|
Injectable,
|
||||||
UnauthorizedException,
|
UnauthorizedException,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
|
import * as dayjs from 'dayjs';
|
||||||
|
import { eq, and, or } from 'drizzle-orm';
|
||||||
import { RedisService } from '@/service/redis/redis.service';
|
import { RedisService } from '@/service/redis/redis.service';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { UtilsService } from '@/common/utils/utils.service';
|
import { UtilsService } from '@/common/utils/utils.service';
|
||||||
import { eq, and, or } from 'drizzle-orm';
|
import { DatabaseService } from '@/service/database/database.service';
|
||||||
|
import { CustomLogger } from '@/common/logger/logger.service';
|
||||||
|
|
||||||
|
|
||||||
import { CheckEmailDto } from './dto/check-email.dto';
|
import { CheckEmailDto } from './dto/check-email.dto';
|
||||||
import { SendEmailCodeDto } from './dto/send-email-code.dto';
|
import { SendEmailCodeDto } from './dto/send-email-code.dto';
|
||||||
import { RegisterDto } from './dto/register.dto';
|
import { RegisterDto } from './dto/register.dto';
|
||||||
@ -15,20 +20,16 @@ import { EmailLoginDto } from './dto/email-login.dto';
|
|||||||
import { UpdateUserDto } from './dto/update-user.dto';
|
import { UpdateUserDto } from './dto/update-user.dto';
|
||||||
import { UpdateProfileDto } from './dto/update-profile.dto';
|
import { UpdateProfileDto } from './dto/update-profile.dto';
|
||||||
import { UpdateSignatureDto } from './dto/update-signature.dto';
|
import { UpdateSignatureDto } from './dto/update-signature.dto';
|
||||||
import * as bcrypt from 'bcrypt';
|
import { ChangePasswordDto } from './dto/change-password.dto';
|
||||||
import * as jwt from 'jsonwebtoken';
|
import { ResetPasswordDto } from './dto/reset-password.dto';
|
||||||
import { DatabaseService } from '@/service/database/database.service';
|
|
||||||
import {
|
import {
|
||||||
user,
|
user,
|
||||||
userPassword,
|
userPassword,
|
||||||
userProfile,
|
userProfile,
|
||||||
userSignatureHistory,
|
userSignatureHistory,
|
||||||
} from '@/drizzle/schema';
|
} from '@/drizzle/schema';
|
||||||
import { CustomLogger } from '@/common/logger/logger.service';
|
|
||||||
import { EmailCodeType } from '@/type/enum';
|
import { EmailCodeType } from '@/type/enum';
|
||||||
import * as dayjs from 'dayjs';
|
|
||||||
import { UserGuard } from '@/type/userGuard';
|
import { UserGuard } from '@/type/userGuard';
|
||||||
import { SQL } from 'drizzle-orm';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserService {
|
export class UserService {
|
||||||
@ -259,29 +260,17 @@ export class UserService {
|
|||||||
return { accessToken, refreshToken };
|
return { accessToken, refreshToken };
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 刷新令牌
|
// 刷新令牌
|
||||||
// async refreshToken(refreshToken: string) {
|
refreshToken(userGuard: UserGuard) {
|
||||||
// try {
|
const { userId, username, email, time } = userGuard;
|
||||||
// const decoded = jwt.verify(
|
const accessToken = this.utils.generateAccessToken({
|
||||||
// refreshToken,
|
userId,
|
||||||
// this.config.get('JWT_SECRET'),
|
username,
|
||||||
// ) as { userId: number };
|
email,
|
||||||
// const storedToken = await this.redis.get(
|
time,
|
||||||
// `refresh_token:${decoded.userId}`,
|
});
|
||||||
// );
|
return { accessToken };
|
||||||
|
}
|
||||||
// if (!storedToken || storedToken !== refreshToken) {
|
|
||||||
// throw new UnauthorizedException('无效的刷新令牌');
|
|
||||||
// }
|
|
||||||
|
|
||||||
// await this.redis.del(`refresh_token:${decoded.userId}`);
|
|
||||||
// await this.redis.del(`access_token:${decoded.userId}`);
|
|
||||||
|
|
||||||
// return this.generateTokens(decoded.userId);
|
|
||||||
// } catch (error) {
|
|
||||||
// throw new UnauthorizedException('无效的刷新令牌');
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 退出登录
|
// // 退出登录
|
||||||
// async logout(userId: number) {
|
// async logout(userId: number) {
|
||||||
@ -367,4 +356,92 @@ export class UserService {
|
|||||||
// await this.logout(userId);
|
// await this.logout(userId);
|
||||||
// return { message: '删除成功' };
|
// return { message: '删除成功' };
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
async changePassword(userId: number, dto: ChangePasswordDto) {
|
||||||
|
const result = await this.database.db
|
||||||
|
.select({
|
||||||
|
userId: user.userId,
|
||||||
|
})
|
||||||
|
.from(user)
|
||||||
|
.where(eq(user.userId, userId))
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
if (result.length === 0) {
|
||||||
|
throw new BadRequestException('用户不存在');
|
||||||
|
}
|
||||||
|
// 比对密码
|
||||||
|
const userData = result[0];
|
||||||
|
const passwordData = await this.database.db
|
||||||
|
.select({
|
||||||
|
passwordHash: userPassword.passwordHash,
|
||||||
|
})
|
||||||
|
.from(userPassword)
|
||||||
|
.where(eq(userPassword.userId, userData.userId))
|
||||||
|
.execute();
|
||||||
|
// 验证旧密码
|
||||||
|
if (
|
||||||
|
!(await this.utils.verifyPassword(
|
||||||
|
dto.oldPassword,
|
||||||
|
passwordData[0].passwordHash,
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
throw new UnauthorizedException('旧密码错误');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密新密码
|
||||||
|
const hashedPassword = await this.utils.hashPassword(dto.newPassword);
|
||||||
|
|
||||||
|
// 更新密码
|
||||||
|
await this.database.db
|
||||||
|
.update(userPassword)
|
||||||
|
.set({
|
||||||
|
passwordHash: hashedPassword,
|
||||||
|
})
|
||||||
|
.where(eq(userPassword.userId, userId));
|
||||||
|
|
||||||
|
return { message: '密码修改成功' };
|
||||||
|
}
|
||||||
|
|
||||||
|
async resetPassword(dto: ResetPasswordDto) {
|
||||||
|
// 邮箱验证码key
|
||||||
|
const codeKey = `${EmailCodeType.resetpassword}:${dto.email}`;
|
||||||
|
this.logger.debug(
|
||||||
|
`邮箱验证码key: ${EmailCodeType.resetpassword}:${dto.email} ${codeKey}`,
|
||||||
|
);
|
||||||
|
// 获取验证码
|
||||||
|
const code = await this.redis.get(codeKey);
|
||||||
|
// 验证码错误或已过期
|
||||||
|
if (!code || code !== dto.code) {
|
||||||
|
throw new BadRequestException('验证码错误或已过期');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await this.database.db
|
||||||
|
.select({
|
||||||
|
userId: user.userId,
|
||||||
|
})
|
||||||
|
.from(user)
|
||||||
|
.where(eq(user.email, dto.email))
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
if (result.length === 0) {
|
||||||
|
throw new BadRequestException('用户不存在');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密新密码
|
||||||
|
const hashedPassword = await this.utils.hashPassword(dto.newPassword);
|
||||||
|
|
||||||
|
// 更新密码
|
||||||
|
// 更新密码
|
||||||
|
await this.database.db
|
||||||
|
.update(userPassword)
|
||||||
|
.set({
|
||||||
|
passwordHash: hashedPassword,
|
||||||
|
})
|
||||||
|
.where(eq(userPassword.userId, result[0].userId));
|
||||||
|
|
||||||
|
// 删除验证码
|
||||||
|
await this.redis.del(codeKey);
|
||||||
|
|
||||||
|
return { message: '密码重置成功' };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ export enum EmailCodeType {
|
|||||||
LOGIN = 'login',
|
LOGIN = 'login',
|
||||||
register = 'REGISTER',
|
register = 'REGISTER',
|
||||||
login = 'LOGIN',
|
login = 'LOGIN',
|
||||||
|
resetpassword = 'RESETPASSWORD',
|
||||||
|
RESETPASSWORD = 'resetpassword',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const allColors = {
|
export const allColors = {
|
||||||
|
Loading…
Reference in New Issue
Block a user