问题: i18n 批量转换 (commit55f216c) 生成了 813 个 hash 键引用但翻译从未写入 JSON, 导致用户在受影响模块看到 hash 键路径(如 monitor.quickquerybarts.s64559524)而非实际文本. 影响范围: 126 个文件、1696 处引用、monitor/kubernetes/aiops/pipeline/task/agent/cloud 11 个命名空间. 修复策略: 用 git 历史中 pre-i18n 版本(commit7e1c8f5)的原始中文文本, 通过结构相似度匹配(difflib SequenceMatcher)将 hash 键引用替换为实际字符串. 对于组件拆分后产生的新文件,映射到其原始源文件查找中文文本. 修复明细: - monitor 命名空间: 335 键修复(站点拨测/日志中心/中间件监控/告警规则/SNMP/主机监控) - kubernetes 命名空间: 195 键修复(资源编辑器/Deployment编辑器/事件/命名空间/Pod日志) - aiops 命名空间: 70 键修复(容量预测/成本预测/告警分析) - pipeline/task/agent/cloud/common 命名空间: 其余键修复 验证: tsc -b=0, npm build✓ (16.87s), ESLint=0/0/0, go build=0, 17/17 测试通过, Docker healthy
172 lines
5.7 KiB
TypeScript
172 lines
5.7 KiB
TypeScript
import i18n from '@/i18n';
|
|
// 站点拨测辅助函数 // Logan.Li
|
|
|
|
export const PROBE_TYPE_OPTIONS = ['HTTP', 'SSL', 'TCP', 'DNS', 'PING'] as const;
|
|
export type ProbeType = (typeof PROBE_TYPE_OPTIONS)[number];
|
|
|
|
export interface SiteTask {
|
|
id: number;
|
|
name: string;
|
|
url: string;
|
|
type: string;
|
|
frequency_sec: number;
|
|
timeout_sec: number;
|
|
expected_status_code: number;
|
|
enabled: boolean;
|
|
last_status?: string;
|
|
last_response_time_ms?: number;
|
|
last_checked_at?: string;
|
|
success_rate?: number;
|
|
alert_status?: 'normal' | 'alerting';
|
|
created_at: string;
|
|
ssl_days_remaining?: number;
|
|
ssl_cert_expiry?: string;
|
|
}
|
|
|
|
export interface SiteResult {
|
|
id: number;
|
|
task_id: number;
|
|
status: string;
|
|
http_status_code?: number;
|
|
response_time?: number;
|
|
total_time: number;
|
|
remote_ip?: string;
|
|
error_message?: string;
|
|
checked_at: string;
|
|
ssl_days_remaining?: number;
|
|
ssl_cert_expiry?: string;
|
|
ssl_cert_issuer?: string;
|
|
}
|
|
|
|
export interface SiteStats {
|
|
total_tasks: number;
|
|
enabled_tasks: number;
|
|
up_count: number;
|
|
down_count: number;
|
|
availability: number;
|
|
probe_count: number;
|
|
}
|
|
|
|
export type SiteTimeRange = '1h' | '6h' | '24h' | '7d';
|
|
|
|
export const SITE_TIME_RANGE_HOURS: Record<SiteTimeRange, number> = {
|
|
'1h': 1,
|
|
'6h': 6,
|
|
'24h': 24,
|
|
'7d': 168,
|
|
};
|
|
|
|
export const SITE_TIME_RANGE_LABELS: Record<SiteTimeRange, string> = {
|
|
'1h': i18n.t('近 1 小时'),
|
|
'6h': i18n.t('monitor.siteProtocol.rangeDefault'),
|
|
'24h': i18n.t('近 24 小时'),
|
|
'7d': i18n.t('近 7 天'),
|
|
};
|
|
|
|
/** 各协议推荐默认拨测间隔(秒)// Logan.Li */
|
|
export const DEFAULT_PROBE_FREQUENCY: Record<ProbeType, number> = {
|
|
HTTP: 60,
|
|
SSL: 3600,
|
|
TCP: 120,
|
|
DNS: 300,
|
|
PING: 120,
|
|
};
|
|
|
|
/** 各协议最小拨测间隔(秒)// Logan.Li */
|
|
export const MIN_PROBE_FREQUENCY: Record<ProbeType, number> = {
|
|
HTTP: 30,
|
|
SSL: 600,
|
|
TCP: 30,
|
|
DNS: 60,
|
|
PING: 30,
|
|
};
|
|
|
|
export function defaultProbeFrequency(type: ProbeType): number {
|
|
return DEFAULT_PROBE_FREQUENCY[type] ?? 60;
|
|
}
|
|
|
|
export const probeTypeMeta: Record<ProbeType, { label: string; placeholder: string; hint: string }> = {
|
|
HTTP: { label: i18n.t('目标 URL'), placeholder: 'https://example.com/health', hint: i18n.t('Web 请求(支持 http/https),检测状态码与响应时间') },
|
|
SSL: { label: i18n.t('SSL 地址'), placeholder: i18n.t('example.com:443 或 https://example.com'), hint: i18n.t('TLS 握手检测证书(支持 host:port / https:// 域名,不发起 HTTP 请求)') },
|
|
TCP: { label: i18n.t('主机:端口'), placeholder: i18n.t('192.168.1.1:8080 或 example.com:443'), hint: i18n.t('TCP 端口连通性') },
|
|
DNS: { label: i18n.t('cloud.domainName'), placeholder: 'example.com', hint: i18n.t('monitor.helpersts.sd3fd8f1d') },
|
|
PING: { label: i18n.t('主机地址'), placeholder: i18n.t('192.168.1.1 或 example.com'), hint: i18n.t('ICMP 连通性与延迟(默认 2 分钟/次)') },
|
|
};
|
|
|
|
export function probeFrequencyHint(type: ProbeType): string {
|
|
const sec = defaultProbeFrequency(type);
|
|
return `推荐 ${fmtFrequency(sec)},最小 ${fmtFrequency(MIN_PROBE_FREQUENCY[type])}`;
|
|
}
|
|
|
|
/** 拨测频率展示 // Logan.Li */
|
|
export function fmtFrequency(sec?: number): string {
|
|
if (!sec) return '--';
|
|
if (sec < 60) return `${sec}秒`;
|
|
if (sec < 3600) return `${Math.round(sec / 60)}分钟`;
|
|
return `${(sec / 3600).toFixed(1)}小时`;
|
|
}
|
|
|
|
/** 可用率 / 响应时间组合展示 // Logan.Li */
|
|
export function fmtAvailResponse(task: Pick<SiteTask, 'success_rate' | 'last_response_time_ms'>): string {
|
|
const avail = task.success_rate != null && task.success_rate >= 0
|
|
? `${task.success_rate.toFixed(1)}%`
|
|
: '--';
|
|
return `${avail} / ${fmtMs(task.last_response_time_ms)}`;
|
|
}
|
|
|
|
export function fmtMs(ms?: number): string {
|
|
if (ms == null) return '--';
|
|
if (ms < 1) return '0ms';
|
|
if (ms < 1000) return `${Math.round(ms)}ms`;
|
|
return `${(ms / 1000).toFixed(2)}s`;
|
|
}
|
|
|
|
/** 列表时间:MM-DD HH:mm // Logan.Li */
|
|
export function fmtTime(iso?: string): string {
|
|
if (!iso) return '--';
|
|
const d = new Date(iso);
|
|
const pad = (n: number) => String(n).padStart(2, '0');
|
|
return `${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
}
|
|
|
|
/** SSL 证书预警阈值(天)// Logan.Li */
|
|
export const SSL_EXPIRY_WARN_DAYS = 30;
|
|
|
|
/** 是否为 SSL 证书拨测任务 // Logan.Li */
|
|
export function isSslProbeTask(task: Pick<SiteTask, 'type'>): boolean {
|
|
const t = task.type?.toUpperCase();
|
|
return t === 'SSL' || t === 'HTTPS';
|
|
}
|
|
|
|
/** 证书是否进入预警期(默认 < 30 天)// Logan.Li */
|
|
export function isSslExpiringSoon(days?: number, threshold = SSL_EXPIRY_WARN_DAYS): boolean {
|
|
return days != null && days < threshold;
|
|
}
|
|
|
|
/** SSL 证书剩余天数展示 // Logan.Li */
|
|
export function fmtSslDays(days?: number): { text: string; tone: string } {
|
|
if (days == null) return { text: '--', tone: 'text-[var(--dg-text-muted)]' };
|
|
if (days < 0) return { text: i18n.t('system.expired2'), tone: 'dg-text-red' };
|
|
if (days <= 7) return { text: `${days}天`, tone: 'dg-text-red' };
|
|
if (days <= 30) return { text: `${days}天`, tone: 'dg-text-yellow' };
|
|
return { text: `${days}天`, tone: 'dg-text-green' };
|
|
}
|
|
|
|
/** 归一化拨测协议类型 // Logan.Li */
|
|
export function normalizeProbeType(raw?: string): ProbeType {
|
|
const upper = (raw || 'HTTP').toUpperCase();
|
|
if (upper === 'HTTPS') return 'HTTP';
|
|
if (PROBE_TYPE_OPTIONS.includes(upper as ProbeType)) return upper as ProbeType;
|
|
return 'HTTP';
|
|
}
|
|
|
|
export function isHttpProbeTask(task: Pick<SiteTask, 'type'>): boolean {
|
|
return normalizeProbeType(task.type) === 'HTTP';
|
|
}
|
|
|
|
export function fmtChartTime(iso?: string): string {
|
|
if (!iso) return '';
|
|
const d = new Date(iso);
|
|
const pad = (n: number) => String(n).padStart(2, '0');
|
|
return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
} |