forked from wangziqi/gongxue-base
feat: add platform audit alert notifications
This commit is contained in:
@@ -5,6 +5,8 @@ import {
|
||||
exportPlatformAuditLogs,
|
||||
loadPlatformAuditAlerts,
|
||||
loadPlatformAuditLogs,
|
||||
loadPlatformAuditNotificationChannels,
|
||||
loadPlatformAuditNotificationEvents,
|
||||
loadPlatformInvoices,
|
||||
loadPlatformOverview,
|
||||
loadPlatformQuestionBankGrants,
|
||||
@@ -13,6 +15,8 @@ import {
|
||||
updatePlatformAuditAlertStatus,
|
||||
type PlatformAuditAlertItem,
|
||||
type PlatformAuditLogItem,
|
||||
type PlatformAuditNotificationChannelItem,
|
||||
type PlatformAuditNotificationEventItem,
|
||||
type PlatformInvoiceItem,
|
||||
type PlatformOverview,
|
||||
type PlatformQuestionBankGrant,
|
||||
@@ -42,6 +46,8 @@ export default function PlatformWorkbenchPage() {
|
||||
const [invoices, setInvoices] = useState<PlatformInvoiceItem[]>([]);
|
||||
const [auditLogs, setAuditLogs] = useState<PlatformAuditLogItem[]>([]);
|
||||
const [auditAlerts, setAuditAlerts] = useState<PlatformAuditAlertItem[]>([]);
|
||||
const [auditNotificationChannels, setAuditNotificationChannels] = useState<PlatformAuditNotificationChannelItem[]>([]);
|
||||
const [auditNotificationEvents, setAuditNotificationEvents] = useState<PlatformAuditNotificationEventItem[]>([]);
|
||||
const [banks, setBanks] = useState<PlatformQuestionBankItem[]>([]);
|
||||
const [grants, setGrants] = useState<PlatformQuestionBankGrant[]>([]);
|
||||
const [error, setError] = useState('');
|
||||
@@ -56,7 +62,9 @@ export default function PlatformWorkbenchPage() {
|
||||
loadPlatformQuestionBankGrants({ limit: 6 }).catch(() => ({ items: [] })),
|
||||
loadPlatformAuditLogs({ limit: 6 }).catch(() => ({ items: [] })),
|
||||
loadPlatformAuditAlerts({ status: 'open', limit: 6 }).catch(() => ({ items: [] })),
|
||||
]).then(([overviewPayload, tenantPayload, invoicePayload, bankPayload, grantPayload, auditPayload, alertPayload]) => {
|
||||
loadPlatformAuditNotificationChannels({ enabled: true, limit: 6 }).catch(() => ({ items: [] })),
|
||||
loadPlatformAuditNotificationEvents({ limit: 6 }).catch(() => ({ items: [] })),
|
||||
]).then(([overviewPayload, tenantPayload, invoicePayload, bankPayload, grantPayload, auditPayload, alertPayload, channelPayload, eventPayload]) => {
|
||||
setOverview(overviewPayload.item || null);
|
||||
setTenants(tenantPayload.items || []);
|
||||
setInvoices(invoicePayload.items || []);
|
||||
@@ -64,6 +72,8 @@ export default function PlatformWorkbenchPage() {
|
||||
setGrants(grantPayload.items || []);
|
||||
setAuditLogs(auditPayload.items || []);
|
||||
setAuditAlerts(alertPayload.items || []);
|
||||
setAuditNotificationChannels(channelPayload.items || []);
|
||||
setAuditNotificationEvents(eventPayload.items || []);
|
||||
}).catch(nextError => setError(nextError instanceof Error ? nextError.message : '平台后台加载失败'));
|
||||
}, []);
|
||||
|
||||
@@ -196,6 +206,30 @@ export default function PlatformWorkbenchPage() {
|
||||
</View>
|
||||
{!auditAlerts.length ? <View className='platform-empty'>暂无开放审计告警。</View> : null}
|
||||
</View>
|
||||
<View className='platform-section'>
|
||||
<Text className='platform-section-title'>告警外部通知</Text>
|
||||
<View className='platform-grid'>
|
||||
<View className='platform-metric'><Text className='platform-metric-label'>启用渠道</Text><Text className='platform-metric-value'>{String(auditNotificationChannels.length)}</Text></View>
|
||||
<View className='platform-metric'><Text className='platform-metric-label'>最近事件</Text><Text className='platform-metric-value'>{String(auditNotificationEvents.length)}</Text></View>
|
||||
<View className='platform-metric'><Text className='platform-metric-label'>失败事件</Text><Text className='platform-metric-value'>{String(auditNotificationEvents.filter(item => item.status === 'failed').length)}</Text></View>
|
||||
<View className='platform-metric'><Text className='platform-metric-label'>待重试</Text><Text className='platform-metric-value'>{String(auditNotificationEvents.filter(item => item.status === 'retrying').length)}</Text></View>
|
||||
</View>
|
||||
<View className='platform-list'>
|
||||
{auditNotificationChannels.map(item => (
|
||||
<View className='platform-row' key={item.id}>
|
||||
<Text className='platform-row-main'>{item.name || item.channelCode || '-'}</Text>
|
||||
<Text className='platform-row-meta'>{item.provider || '-'} · {item.minSeverity || '-'} · {item.webhook?.host || '-'} · {item.statusFilter?.join('/') || '-'}</Text>
|
||||
</View>
|
||||
))}
|
||||
{auditNotificationEvents.map(item => (
|
||||
<View className='platform-row' key={item.id}>
|
||||
<Text className='platform-row-main'>{item.alertTitle || item.alertAction || '-'}</Text>
|
||||
<Text className='platform-row-meta'>{item.channelName || item.channelCode || '-'} · {item.status || '-'} · HTTP {String(item.lastHttpCode || '-')} · {item.tenantName || item.tenantSlug || '平台'}</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
{!auditNotificationChannels.length && !auditNotificationEvents.length ? <View className='platform-empty'>暂无外部通知渠道或发送事件。</View> : null}
|
||||
</View>
|
||||
{error ? <Text className='platform-error'>{error}</Text> : null}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -281,6 +281,53 @@ export interface PlatformAuditAlertItem {
|
||||
updatedAt?: string | null;
|
||||
}
|
||||
|
||||
export interface PlatformAuditNotificationChannelItem {
|
||||
id: string;
|
||||
channelCode?: string | null;
|
||||
name?: string | null;
|
||||
description?: string | null;
|
||||
enabled?: boolean | null;
|
||||
provider?: string | null;
|
||||
secretRef?: string | null;
|
||||
minSeverity?: string | null;
|
||||
statusFilter?: string[] | null;
|
||||
actionPatterns?: string[] | null;
|
||||
tenantIds?: string[] | null;
|
||||
timeoutSec?: number | string | null;
|
||||
webhook?: {
|
||||
protocol?: string | null;
|
||||
host?: string | null;
|
||||
pathname?: string | null;
|
||||
} | null;
|
||||
metadata?: Record<string, unknown> | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
}
|
||||
|
||||
export interface PlatformAuditNotificationEventItem {
|
||||
id: string;
|
||||
channelId?: string | null;
|
||||
channelCode?: string | null;
|
||||
channelName?: string | null;
|
||||
alertId?: string | null;
|
||||
auditLogId?: string | null;
|
||||
provider?: string | null;
|
||||
status?: string | null;
|
||||
attempts?: number | string | null;
|
||||
lastHttpCode?: number | string | null;
|
||||
lastError?: string | null;
|
||||
lastResponseSummary?: string | null;
|
||||
alertSeverity?: string | null;
|
||||
alertStatus?: string | null;
|
||||
alertTitle?: string | null;
|
||||
alertAction?: string | null;
|
||||
tenantName?: string | null;
|
||||
tenantSlug?: string | null;
|
||||
sentAt?: string | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
}
|
||||
|
||||
export interface CreatePlatformTenantInput {
|
||||
slug: string;
|
||||
name: string;
|
||||
@@ -450,6 +497,30 @@ export async function updatePlatformAuditAlertStatus(input: {
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadPlatformAuditNotificationChannels(query: {
|
||||
enabled?: boolean;
|
||||
provider?: string;
|
||||
limit?: number;
|
||||
} = {}) {
|
||||
return apiRequest<{ items?: PlatformAuditNotificationChannelItem[] }>('/api/platform-admin/audit-notification-channels', {
|
||||
query: { ...query, limit: query.limit || 50 },
|
||||
tenantId: null,
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadPlatformAuditNotificationEvents(query: {
|
||||
channelId?: string;
|
||||
alertId?: string;
|
||||
status?: string;
|
||||
provider?: string;
|
||||
limit?: number;
|
||||
} = {}) {
|
||||
return apiRequest<{ items?: PlatformAuditNotificationEventItem[] }>('/api/platform-admin/audit-notification-events', {
|
||||
query: { ...query, limit: query.limit || 50 },
|
||||
tenantId: null,
|
||||
});
|
||||
}
|
||||
|
||||
export async function loadPlatformInvoices(query: { tenantId?: string; status?: string; limit?: number } = {}) {
|
||||
return apiRequest<{ items?: PlatformInvoiceItem[] }>('/api/platform-admin/invoices', {
|
||||
query: { ...query, limit: query.limit || 80 },
|
||||
|
||||
Reference in New Issue
Block a user