feat: 完善服务监控与下游重投
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user