feat: 完善服务监控与下游重投

This commit is contained in:
hectorzhao
2026-08-14 17:21:29 +08:00
parent d30d9ea4d0
commit 1ef4380422
50 changed files with 1279 additions and 82 deletions
@@ -9,6 +9,7 @@ import { SecurityDetectionService } from './security-detection.service';
export class SecurityDetectionController {
constructor(private readonly security: SecurityDetectionService) {}
@Get('overview') overview(@Query('range') range?: string) { return this.security.overview(range); }
@Get('notification-summary') notificationSummary() { return this.security.notificationSummary(); }
@Get('alerts') alerts(@Query() query: Record<string, string>) { return this.security.listAlerts(query); }
@Get('rules') rules() { return this.security.listRules(); }
@Put('rules/:id') @RequireRecentAuthentication() updateRule(@Param('id') id: string, @Body() body: Record<string, unknown>, @CurrentSessionUserId() userId: string) { return this.security.updateRule(id, body, userId); }
@@ -11,7 +11,7 @@ function createPrisma() {
const prisma = {
securityDetectionRule: { findUnique: jest.fn(), findMany: jest.fn() },
securityDetectionEvent: { count: jest.fn() },
securityAlert: { findUnique: jest.fn(), update: jest.fn() },
securityAlert: { findUnique: jest.fn(), update: jest.fn(), count: jest.fn() },
securityBlock: { create: jest.fn(), update: jest.fn() },
securityProtectedNetwork: { findMany: jest.fn().mockResolvedValue([]) },
operationLog: { create: jest.fn() },
@@ -21,6 +21,16 @@ function createPrisma() {
}
describe('SecurityDetectionService', () => {
it('returns an independent active and critical alert summary for the global bell', async () => {
const { prisma } = createPrisma();
prisma.securityAlert.count.mockResolvedValueOnce(4).mockResolvedValueOnce(2);
const service = new SecurityDetectionService(prisma as never, {} as never);
await expect(service.notificationSummary()).resolves.toEqual({ count: 4, criticalCount: 2 });
expect(prisma.securityAlert.count).toHaveBeenNthCalledWith(1, { where: { status: { in: ['open', 'acknowledged', 'block_failed'] } } });
expect(prisma.securityAlert.count).toHaveBeenNthCalledWith(2, { where: { status: { in: ['open', 'acknowledged', 'block_failed'] }, severity: 'critical' } });
});
it('keeps a below-threshold event without creating a false alert', async () => {
const { prisma, tx } = createPrisma();
prisma.securityDetectionRule.findUnique.mockResolvedValue({ id: 'rule-1', enabled: true, threshold: 3, windowSeconds: 60, cooldownSeconds: 60, severity: 'high' });
@@ -94,6 +94,15 @@ export class SecurityDetectionService {
};
}
async notificationSummary() {
const activeStatuses = ['open', 'acknowledged', 'block_failed'];
const [count, criticalCount] = await Promise.all([
this.prisma.securityAlert.count({ where: { status: { in: activeStatuses } } }),
this.prisma.securityAlert.count({ where: { status: { in: activeStatuses }, severity: 'critical' } }),
]);
return { count, criticalCount };
}
listAlerts(query: { status?: string; ruleCode?: string; sourceIp?: string; page?: string; pageSize?: string }) {
const page = positiveInt(query.page, 1, 100000);
const pageSize = positiveInt(query.pageSize, 20, 100);