import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Button, Checkbox, Form, Modal, Select, Space } from 'antd'; import { DownloadOutlined, SyncOutlined } from '@ant-design/icons'; import { HttpUtil, FileManager, PromiseUtil } from '@/utils'; import { useMediaQuery } from '@/hooks/useMediaQuery'; import './LogModal.css'; interface LogModalProps { open: boolean; onClose: () => void; } interface ParsedLog { date: string; time: string; stamp: string; levelText: string; levelClass: string; service: string; body: string; } const LEVELS = ['DEBUG', 'INFO', 'NOTICE', 'WARNING', 'ERROR']; const LEVEL_CLASSES = ['level-debug', 'level-info', 'level-notice', 'level-warning', 'level-error']; function parseLogLine(line: string): ParsedLog { const [head, ...rest] = (line || '').split(' - '); const message = rest.join(' - '); const parts = head.split(' '); let date = ''; let time = ''; let levelText: string; if (parts.length >= 3) { [date, time, levelText] = parts; } else { levelText = head; } const li = LEVELS.indexOf(levelText); const levelClass = li >= 0 ? LEVEL_CLASSES[li] : 'level-unknown'; let service = ''; let body = message || ''; if (body.startsWith('XRAY:')) { service = 'XRAY:'; body = body.slice('XRAY:'.length).trimStart(); } else if (body) { service = 'X-UI:'; } const stamp = [date, time].filter(Boolean).join(' '); return { date, time, stamp, levelText, levelClass, service, body }; } export default function LogModal({ open, onClose }: LogModalProps) { const { t } = useTranslation(); const { isMobile } = useMediaQuery(); const [rows, setRows] = useState('20'); const [level, setLevel] = useState('info'); const [syslog, setSyslog] = useState(false); const [loading, setLoading] = useState(false); const [logs, setLogs] = useState([]); const openRef = useRef(open); const refresh = useCallback(async () => { setLoading(true); try { const msg = await HttpUtil.post(`/panel/api/server/logs/${rows}`, { level, syslog, }); if (msg?.success) { setLogs(msg.obj || []); } await PromiseUtil.sleep(300); } finally { setLoading(false); } }, [rows, level, syslog]); useEffect(() => { openRef.current = open; if (open) refresh(); }, [open, refresh]); useEffect(() => { if (openRef.current) refresh(); }, [rows, level, syslog, refresh]); const parsedLogs = useMemo(() => logs.map(parseLogLine), [logs]); function download() { FileManager.downloadTextFile(logs.join('\n'), 'x-ui.log'); } const titleNode = ( <> {t('pages.index.logs')} ); return (
setSyslog(e.target.checked)}> SysLog