fix(admin-service): 修复维护拦截器路径检测和错误处理
问题:添加系统维护检测后站内通知功能失效 修复: 1. 使用 request.url 获取完整路径(包含 /api/v1 前缀) 2. 同时支持带前缀和不带前缀的路径检测 3. 添加 try-catch 错误处理,数据库错误时放行请求而非阻断 4. 添加日志记录便于调试 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c392142562
commit
fea0b42223
|
|
@ -6,6 +6,7 @@ import {
|
|||
HttpException,
|
||||
HttpStatus,
|
||||
Inject,
|
||||
Logger,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
import {
|
||||
|
|
@ -19,6 +20,8 @@ import {
|
|||
*/
|
||||
@Injectable()
|
||||
export class MaintenanceInterceptor implements NestInterceptor {
|
||||
private readonly logger = new Logger(MaintenanceInterceptor.name);
|
||||
|
||||
constructor(
|
||||
@Inject(SYSTEM_MAINTENANCE_REPOSITORY)
|
||||
private readonly maintenanceRepo: ISystemMaintenanceRepository,
|
||||
|
|
@ -28,46 +31,65 @@ export class MaintenanceInterceptor implements NestInterceptor {
|
|||
context: ExecutionContext,
|
||||
next: CallHandler,
|
||||
): Promise<Observable<any>> {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const path = request.path;
|
||||
try {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
// 获取请求路径,优先使用 url(去除查询参数)
|
||||
const path = (request.url?.split('?')[0] || request.path) as string;
|
||||
|
||||
// 白名单路径 - 这些路径在维护期间也可以访问
|
||||
const whitelist = [
|
||||
'/mobile/system/maintenance-status', // 维护状态检查
|
||||
'/health', // 健康检查
|
||||
'/api/health', // 健康检查(带前缀)
|
||||
];
|
||||
// 白名单路径 - 这些路径在维护期间也可以访问
|
||||
// 支持带 /api/v1 前缀和不带前缀的两种情况
|
||||
const whitelist = [
|
||||
'/api/v1/mobile/system/maintenance-status',
|
||||
'/mobile/system/maintenance-status',
|
||||
'/api/v1/health',
|
||||
'/health',
|
||||
];
|
||||
|
||||
// 只拦截移动端 API(以 /mobile 开头的路径)
|
||||
if (!path.startsWith('/mobile')) {
|
||||
return next.handle();
|
||||
}
|
||||
// 检查是否是移动端 API(需要同时检查带前缀和不带前缀的路径)
|
||||
const isMobileApi =
|
||||
path.includes('/mobile/') ||
|
||||
path.startsWith('/mobile');
|
||||
|
||||
// 检查是否在白名单中
|
||||
if (whitelist.some((p) => path.startsWith(p))) {
|
||||
return next.handle();
|
||||
}
|
||||
// 非移动端 API,直接放行
|
||||
if (!isMobileApi) {
|
||||
return next.handle();
|
||||
}
|
||||
|
||||
// 检查是否在维护中
|
||||
const activeMaintenance = await this.maintenanceRepo.findActiveMaintenance();
|
||||
// 检查是否在白名单中
|
||||
if (whitelist.some((p) => path === p || path.startsWith(p + '/'))) {
|
||||
return next.handle();
|
||||
}
|
||||
|
||||
if (activeMaintenance) {
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.SERVICE_UNAVAILABLE,
|
||||
error: 'Service Unavailable',
|
||||
message: '系统维护中',
|
||||
maintenance: {
|
||||
inMaintenance: true,
|
||||
title: activeMaintenance.title,
|
||||
message: activeMaintenance.message,
|
||||
endTime: activeMaintenance.endTime,
|
||||
// 检查是否在维护中
|
||||
const activeMaintenance = await this.maintenanceRepo.findActiveMaintenance();
|
||||
|
||||
if (activeMaintenance) {
|
||||
this.logger.warn(`系统维护中,阻断请求: ${path}`);
|
||||
throw new HttpException(
|
||||
{
|
||||
statusCode: HttpStatus.SERVICE_UNAVAILABLE,
|
||||
error: 'Service Unavailable',
|
||||
message: '系统维护中',
|
||||
maintenance: {
|
||||
inMaintenance: true,
|
||||
title: activeMaintenance.title,
|
||||
message: activeMaintenance.message,
|
||||
endTime: activeMaintenance.endTime,
|
||||
},
|
||||
},
|
||||
},
|
||||
HttpStatus.SERVICE_UNAVAILABLE,
|
||||
);
|
||||
}
|
||||
HttpStatus.SERVICE_UNAVAILABLE,
|
||||
);
|
||||
}
|
||||
|
||||
return next.handle();
|
||||
return next.handle();
|
||||
} catch (error) {
|
||||
// 如果是我们抛出的 HttpException,直接重新抛出
|
||||
if (error instanceof HttpException) {
|
||||
throw error;
|
||||
}
|
||||
// 其他错误(如数据库错误),记录日志但不阻断请求
|
||||
this.logger.error(`维护状态检查失败,放行请求: ${error.message}`);
|
||||
return next.handle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue