This commit is contained in:
2025-06-22 21:17:39 +08:00
parent 0eed6cfdbf
commit 063181da5a
16 changed files with 72 additions and 50 deletions

View File

@@ -25,7 +25,7 @@ import { AuthGuard } from '@nestjs/passport';
@UseGuards(AuthGuard('jwt'), RolesGuard) @UseGuards(AuthGuard('jwt'), RolesGuard)
@Roles(Role.Admin) @Roles(Role.Admin)
export class AdminUserController { export class AdminUserController {
constructor(private readonly userService: UserService) { } constructor(private readonly userService: UserService) {}
@Get() @Get()
async list(@Query() listDto: ListDto) { async list(@Query() listDto: ListDto) {

View File

@@ -20,7 +20,7 @@ import { ResourceService } from 'src/resource/resource.service';
@UseGuards(AuthGuard('jwt'), RolesGuard) @UseGuards(AuthGuard('jwt'), RolesGuard)
@Roles(Role.Admin) @Roles(Role.Admin)
export class AdminWebResourceController { export class AdminWebResourceController {
constructor(private readonly resourceService: ResourceService) { } constructor(private readonly resourceService: ResourceService) {}
@Get() @Get()
async list() { async list() {

View File

@@ -30,10 +30,12 @@ import { ThrottlerModule } from '@nestjs/throttler';
}), }),
PassportModule.register({ defaultStrategy: 'jwt' }), PassportModule.register({ defaultStrategy: 'jwt' }),
ThrottlerModule.forRoot({ ThrottlerModule.forRoot({
throttlers: [{ throttlers: [
{
limit: 1000, limit: 1000,
ttl: 60000, // 1 minute ttl: 60000, // 1 minute
}], },
],
}), }),
UserModule, UserModule,
AuthModule, AuthModule,
@@ -47,4 +49,4 @@ import { ThrottlerModule } from '@nestjs/throttler';
controllers: [AppController], controllers: [AppController],
providers: [AppService], providers: [AppService],
}) })
export class AppModule { } export class AppModule {}

View File

@@ -17,7 +17,7 @@ export class AuthController {
constructor( constructor(
private readonly authService: AuthService, private readonly authService: AuthService,
private readonly userSessionService: UserSessionService, private readonly userSessionService: UserSessionService,
) { } ) {}
@Post('login') @Post('login')
@UseGuards(ThrottlerGuard) @UseGuards(ThrottlerGuard)

View File

@@ -10,7 +10,6 @@ import { JwtStrategy } from './strategies/jwt.strategy';
import { ConfigModule, ConfigService } from '@nestjs/config'; import { ConfigModule, ConfigService } from '@nestjs/config';
import { VerificationModule } from 'src/verification/verification.module'; import { VerificationModule } from 'src/verification/verification.module';
import { OptionalAuthGuard } from './strategies/OptionalAuthGuard'; import { OptionalAuthGuard } from './strategies/OptionalAuthGuard';
import { NotificationModule } from 'src/notification/notification.module';
@Module({ @Module({
imports: [ imports: [

View File

@@ -1,4 +1,8 @@
import { BadRequestException, Injectable, UnauthorizedException } from '@nestjs/common'; import {
BadRequestException,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport'; import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt'; import { ExtractJwt, Strategy } from 'passport-jwt';
@@ -37,7 +41,7 @@ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
return { return {
...user, ...user,
sessionId sessionId,
}; };
} }
} }

View File

@@ -3,7 +3,6 @@ import {
Body, Body,
Controller, Controller,
Get, Get,
Ip,
Param, Param,
ParseUUIDPipe, ParseUUIDPipe,
Post, Post,
@@ -21,7 +20,7 @@ export class BlogController {
constructor( constructor(
private readonly blogService: BlogService, private readonly blogService: BlogService,
private readonly userService: UserService, private readonly userService: UserService,
) { } ) {}
@Get() @Get()
getBlogs() { getBlogs() {

View File

@@ -11,7 +11,7 @@ export class BlogService {
private readonly blogRepository: Repository<Blog>, private readonly blogRepository: Repository<Blog>,
@InjectRepository(BlogComment) @InjectRepository(BlogComment)
private readonly blogCommentRepository: Repository<BlogComment>, private readonly blogCommentRepository: Repository<BlogComment>,
) { } ) {}
async list() { async list() {
return this.blogRepository.find({ return this.blogRepository.find({

View File

@@ -1,30 +1,34 @@
import { BadRequestException, CanActivate, ExecutionContext, ForbiddenException, Injectable } from '@nestjs/common'; import {
BadRequestException,
CanActivate,
ExecutionContext,
ForbiddenException,
Injectable,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core'; import { Reflector } from '@nestjs/core';
import { Role } from 'src/auth/role.enum'; import { Role } from 'src/auth/role.enum';
import { User } from 'src/user/entities/user.entity'; import { User } from 'src/user/entities/user.entity';
@Injectable() @Injectable()
export class RolesGuard implements CanActivate { export class RolesGuard implements CanActivate {
constructor( constructor(private reflector: Reflector) {}
private reflector: Reflector,
) { }
async canActivate(context: ExecutionContext): Promise<boolean> { async canActivate(context: ExecutionContext): Promise<boolean> {
const requiredRoles = this.reflector.getAllAndOverride<Role[] | undefined>('roles', [ const requiredRoles = this.reflector.getAllAndOverride<Role[] | undefined>(
context.getHandler(), 'roles',
context.getClass(), [context.getHandler(), context.getClass()],
]); );
if (!requiredRoles) return true; if (!requiredRoles) return true;
const request = context.switchToHttp().getRequest(); const request = context.switchToHttp().getRequest();
const user = request.user as (User | void); const user = request.user as User | void;
if (!user) { if (!user) {
throw new BadRequestException('服务器内部错误'); throw new BadRequestException('服务器内部错误');
} }
if (!requiredRoles.some(role => user.roles.includes(role))) { if (!requiredRoles.some((role) => user.roles.includes(role))) {
throw new ForbiddenException('权限不足'); throw new ForbiddenException('权限不足');
} }

View File

@@ -1,14 +1,13 @@
import { BadRequestException, Injectable } from '@nestjs/common'; import { BadRequestException, Injectable } from '@nestjs/common';
import Dm20151123, * as $Dm20151123 from '@alicloud/dm20151123'; import Dm20151123, * as $Dm20151123 from '@alicloud/dm20151123';
import OpenApi, * as $OpenApi from '@alicloud/openapi-client'; import * as $OpenApi from '@alicloud/openapi-client';
import Client, * as $dm from "@alicloud/dm20151123"; // import Client, * as $dm from '@alicloud/dm20151123';
import Util, * as $Util from '@alicloud/tea-util'; import * as $Util from '@alicloud/tea-util';
import Credential, { Config } from '@alicloud/credentials'; import Credential, { Config } from '@alicloud/credentials';
@Injectable() @Injectable()
export class NotificationService { export class NotificationService {
private dm: Dm20151123; private dm: Dm20151123;
constructor() { constructor() {
@@ -23,7 +22,7 @@ export class NotificationService {
this.dm = new Dm20151123(config); this.dm = new Dm20151123(config);
} }
private getMailHtmlBody(option: { type: 'login-verify', code: string }) { private getMailHtmlBody(option: { type: 'login-verify'; code: string }) {
if (option.type === 'login-verify') { if (option.type === 'login-verify') {
return `<!DOCTYPE html> return `<!DOCTYPE html>
<html> <html>
@@ -76,25 +75,31 @@ export class NotificationService {
</div> </div>
</div> </div>
</body> </body>
</html>` </html>`;
} else { } else {
throw new Error('未配置的模版'); throw new Error('未配置的模版');
} }
} }
async sendMail(option: {
async sendMail(option: { type: 'login-verify', targetMail: string, code: string; }) { type: 'login-verify';
targetMail: string;
code: string;
}) {
const runtime = new $Util.RuntimeOptions({}); const runtime = new $Util.RuntimeOptions({});
const singleSendMailRequest = new $Dm20151123.SingleSendMailRequest({ const singleSendMailRequest = new $Dm20151123.SingleSendMailRequest({
accountName: "security@tonesc.cn", accountName: 'security@tonesc.cn',
addressType: 1, addressType: 1,
replyToAddress: false, replyToAddress: false,
toAddress: `${option.targetMail}`, toAddress: `${option.targetMail}`,
subject: "【特恩的日志】登陆验证码", subject: '【特恩的日志】登陆验证码',
htmlBody: this.getMailHtmlBody({ type: 'login-verify', code: option.code }), htmlBody: this.getMailHtmlBody({
textBody: "", type: 'login-verify',
}) code: option.code,
}),
textBody: '',
});
try { try {
await this.dm.singleSendMailWithOptions(singleSendMailRequest, runtime); await this.dm.singleSendMailWithOptions(singleSendMailRequest, runtime);

View File

@@ -18,7 +18,7 @@ export class UserService {
constructor( constructor(
@InjectRepository(User) @InjectRepository(User)
private readonly userRepository: Repository<User>, private readonly userRepository: Repository<User>,
) { } ) {}
/** /**
* @deprecated 尽量不使用该方法 * @deprecated 尽量不使用该方法
@@ -40,7 +40,7 @@ export class UserService {
return this.userRepository.findOne({ return this.userRepository.findOne({
where: { where: {
userId, userId,
} },
}); });
} }

View File

@@ -1,11 +1,17 @@
import { BadRequestException, Body, Controller, Post, UseGuards } from '@nestjs/common'; import {
BadRequestException,
Body,
Controller,
Post,
UseGuards,
} from '@nestjs/common';
import { SendVerificationCodeDto } from './dto/send-verification-code.dto'; import { SendVerificationCodeDto } from './dto/send-verification-code.dto';
import { VerificationService } from './verification.service'; import { VerificationService } from './verification.service';
import { Throttle, ThrottlerGuard } from '@nestjs/throttler'; import { Throttle, ThrottlerGuard } from '@nestjs/throttler';
@Controller('verification') @Controller('verification')
export class VerificationController { export class VerificationController {
constructor(private readonly verificationService: VerificationService) { } constructor(private readonly verificationService: VerificationService) {}
@Post('send') @Post('send')
@UseGuards(ThrottlerGuard) @UseGuards(ThrottlerGuard)

View File

@@ -5,7 +5,7 @@ import { NotificationService } from 'src/notification/notification.service';
export class VerificationService { export class VerificationService {
private readonly logger = new Logger(VerificationService.name); private readonly logger = new Logger(VerificationService.name);
constructor(private readonly notificationService: NotificationService) { } constructor(private readonly notificationService: NotificationService) {}
private pool: Map< private pool: Map<
string, string,
@@ -51,10 +51,12 @@ export class VerificationService {
this.saveCode(key, code); this.saveCode(key, code);
this.logger.log(`Email[${email}] code: ${code}`); this.logger.log(`Email[${email}] code: ${code}`);
// 发送验证码 // 发送验证码
await this.notificationService.sendMail({ type: 'login-verify', targetMail: email, code, }).catch(() => { await this.notificationService
.sendMail({ type: 'login-verify', targetMail: email, code })
.catch(() => {
this.clearCode(key); this.clearCode(key);
throw new BadRequestException('发送失败,请稍后再试'); throw new BadRequestException('发送失败,请稍后再试');
}) });
return true; return true;
} }

View File

@@ -53,7 +53,7 @@ export default function Page() {
refreshSTSToken: async () => { refreshSTSToken: async () => {
await storeMeta.refresh(); await storeMeta.refresh();
if (!storeMeta.stsTokenData) throw new Error(); if (!storeMeta.stsTokenData) throw new Error();
const { AccessKeyId, AccessKeySecret, SecurityToken } = data; const { AccessKeyId, AccessKeySecret, SecurityToken } = storeMeta.stsTokenData;
return { return {
accessKeyId: AccessKeyId, accessKeyId: AccessKeyId,
accessKeySecret: AccessKeySecret, accessKeySecret: AccessKeySecret,
@@ -65,6 +65,7 @@ export default function Page() {
ossStore.setStore(store); ossStore.setStore(store);
ossStore.setWorkDir(`tone-page/${data.userId}`) ossStore.setWorkDir(`tone-page/${data.userId}`)
ossStore.loadObjectList(); ossStore.loadObjectList();
// eslint-disable-next-line react-hooks/exhaustive-deps -- storeMeta引用会导致无限循环依赖stsTokenData即可
}, [storeMeta.stsTokenData]); }, [storeMeta.stsTokenData]);
const handleRefreshFileList = async () => ossStore.loadObjectList().catch(e => toast.error(e.message)); const handleRefreshFileList = async () => ossStore.loadObjectList().catch(e => toast.error(e.message));