feat: reports page with formatted daily report cards
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
/** Безопасный вывод HTML из агента (только базовая разметка). */
|
||||
export function sanitizeAgentHtml(html: string): string {
|
||||
const allowed = /^(b|strong|i|em|u|code|pre|br|p|div|span|ul|ol|li|hr)$/i;
|
||||
return html.replace(/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, (tag, name: string) => {
|
||||
if (allowed.test(name)) {
|
||||
if (tag.toLowerCase().startsWith("</")) {
|
||||
return `</${name.toLowerCase()}>`;
|
||||
}
|
||||
if (name.toLowerCase() === "br") {
|
||||
return "<br>";
|
||||
}
|
||||
return `<${name.toLowerCase()}>`;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
}
|
||||
|
||||
export interface DailyReportStats {
|
||||
successful_ssh?: number;
|
||||
failed_ssh?: number;
|
||||
sudo_commands?: number;
|
||||
active_bans?: number;
|
||||
active_sessions?: number;
|
||||
active_sessions_rdp?: number;
|
||||
unique_users?: string[];
|
||||
}
|
||||
|
||||
export function isDailyReportType(type: string): boolean {
|
||||
return type === "report.daily.ssh" || type === "report.daily.rdp";
|
||||
}
|
||||
|
||||
export function reportStatsFromDetails(details: Record<string, unknown> | null): DailyReportStats | null {
|
||||
if (!details || typeof details.stats !== "object" || details.stats === null) {
|
||||
return null;
|
||||
}
|
||||
return details.stats as DailyReportStats;
|
||||
}
|
||||
|
||||
export function reportHtmlFromDetails(details: Record<string, unknown> | null): string | null {
|
||||
if (!details) return null;
|
||||
const html = details.report_html;
|
||||
return typeof html === "string" && html.trim() ? html : null;
|
||||
}
|
||||
|
||||
export function reportBodyFromDetails(details: Record<string, unknown> | null): string | null {
|
||||
if (!details) return null;
|
||||
const body = details.report_body;
|
||||
return typeof body === "string" && body.trim() ? body : null;
|
||||
}
|
||||
Reference in New Issue
Block a user