feat(logs): add auto-update toggle to Access Logs and Logs viewers

A checkbox in both the Xray Access Logs and panel Logs modals polls the
existing refresh every 5s while enabled, respecting the current row count,
level/filter, and Direct/Blocked/Proxy selections. The poller tears down on
close or untoggle. Adds a localized pages.index.autoUpdate key to all 13 locales.
This commit is contained in:
MHSanaei
2026-06-26 00:43:32 +02:00
parent 30796dc2ce
commit 9381fa284b
15 changed files with 47 additions and 0 deletions
+17
View File
@@ -13,12 +13,15 @@ interface LogModalProps {
onClose: () => void; onClose: () => void;
} }
const AUTO_UPDATE_INTERVAL = 5000;
export default function LogModal({ open, onClose }: LogModalProps) { export default function LogModal({ open, onClose }: LogModalProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const { isMobile } = useMediaQuery(); const { isMobile } = useMediaQuery();
const [rows, setRows] = useState('20'); const [rows, setRows] = useState('20');
const [level, setLevel] = useState('info'); const [level, setLevel] = useState('info');
const [syslog, setSyslog] = useState(false); const [syslog, setSyslog] = useState(false);
const [autoUpdate, setAutoUpdate] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [logs, setLogs] = useState<string[]>([]); const [logs, setLogs] = useState<string[]>([]);
const openRef = useRef(open); const openRef = useRef(open);
@@ -39,6 +42,11 @@ export default function LogModal({ open, onClose }: LogModalProps) {
} }
}, [rows, level, syslog]); }, [rows, level, syslog]);
const refreshRef = useRef(refresh);
useEffect(() => {
refreshRef.current = refresh;
}, [refresh]);
useEffect(() => { useEffect(() => {
openRef.current = open; openRef.current = open;
if (open) refresh(); if (open) refresh();
@@ -48,6 +56,12 @@ export default function LogModal({ open, onClose }: LogModalProps) {
if (openRef.current) refresh(); if (openRef.current) refresh();
}, [rows, level, syslog, refresh]); }, [rows, level, syslog, refresh]);
useEffect(() => {
if (!open || !autoUpdate) return;
const id = setInterval(() => refreshRef.current(), AUTO_UPDATE_INTERVAL);
return () => clearInterval(id);
}, [open, autoUpdate]);
const parsedLogs = useMemo(() => logs.map(parseLogLine), [logs]); const parsedLogs = useMemo(() => logs.map(parseLogLine), [logs]);
function download() { function download() {
@@ -106,6 +120,9 @@ export default function LogModal({ open, onClose }: LogModalProps) {
<Checkbox checked={syslog} onChange={(e) => setSyslog(e.target.checked)}> <Checkbox checked={syslog} onChange={(e) => setSyslog(e.target.checked)}>
SysLog SysLog
</Checkbox> </Checkbox>
<Checkbox checked={autoUpdate} onChange={(e) => setAutoUpdate(e.target.checked)}>
{t('pages.index.autoUpdate')}
</Checkbox>
</Form.Item> </Form.Item>
<Form.Item className="download-item"> <Form.Item className="download-item">
<Button type="primary" onClick={download} icon={<DownloadOutlined />} /> <Button type="primary" onClick={download} icon={<DownloadOutlined />} />
+17
View File
@@ -44,6 +44,8 @@ function shortTime(value?: string | number): string {
return `${hh}:${mm}:${ss}`; return `${hh}:${mm}:${ss}`;
} }
const AUTO_UPDATE_INTERVAL = 5000;
export default function XrayLogModal({ open, onClose }: XrayLogModalProps) { export default function XrayLogModal({ open, onClose }: XrayLogModalProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const { datepicker } = useDatepicker(); const { datepicker } = useDatepicker();
@@ -53,6 +55,7 @@ export default function XrayLogModal({ open, onClose }: XrayLogModalProps) {
const [showDirect, setShowDirect] = useState(true); const [showDirect, setShowDirect] = useState(true);
const [showBlocked, setShowBlocked] = useState(true); const [showBlocked, setShowBlocked] = useState(true);
const [showProxy, setShowProxy] = useState(true); const [showProxy, setShowProxy] = useState(true);
const [autoUpdate, setAutoUpdate] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [logs, setLogs] = useState<XrayLogEntry[]>([]); const [logs, setLogs] = useState<XrayLogEntry[]>([]);
const openRef = useRef(open); const openRef = useRef(open);
@@ -75,6 +78,11 @@ export default function XrayLogModal({ open, onClose }: XrayLogModalProps) {
} }
}, [rows, filter, showDirect, showBlocked, showProxy]); }, [rows, filter, showDirect, showBlocked, showProxy]);
const refreshRef = useRef(refresh);
useEffect(() => {
refreshRef.current = refresh;
}, [refresh]);
useEffect(() => { useEffect(() => {
openRef.current = open; openRef.current = open;
if (open) refresh(); if (open) refresh();
@@ -84,6 +92,12 @@ export default function XrayLogModal({ open, onClose }: XrayLogModalProps) {
if (openRef.current) refresh(); if (openRef.current) refresh();
}, [rows, showDirect, showBlocked, showProxy, refresh]); }, [rows, showDirect, showBlocked, showProxy, refresh]);
useEffect(() => {
if (!open || !autoUpdate) return;
const id = setInterval(() => refreshRef.current(), AUTO_UPDATE_INTERVAL);
return () => clearInterval(id);
}, [open, autoUpdate]);
function fullDate(value?: string | number): string { function fullDate(value?: string | number): string {
return IntlUtil.formatDate(value, datepicker); return IntlUtil.formatDate(value, datepicker);
} }
@@ -158,6 +172,9 @@ export default function XrayLogModal({ open, onClose }: XrayLogModalProps) {
<Checkbox checked={showProxy} onChange={(e) => setShowProxy(e.target.checked)}> <Checkbox checked={showProxy} onChange={(e) => setShowProxy(e.target.checked)}>
Proxy Proxy
</Checkbox> </Checkbox>
<Checkbox checked={autoUpdate} onChange={(e) => setAutoUpdate(e.target.checked)}>
{t('pages.index.autoUpdate')}
</Checkbox>
</Form.Item> </Form.Item>
<Form.Item className="download-item"> <Form.Item className="download-item">
<Button type="primary" onClick={download} icon={<DownloadOutlined />} /> <Button type="primary" onClick={download} icon={<DownloadOutlined />} />
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "التثبيت شغال، متعملش Refresh للصفحة", "dontRefresh": "التثبيت شغال، متعملش Refresh للصفحة",
"logs": "السجلات", "logs": "السجلات",
"accessLogs": "سجلات الوصول", "accessLogs": "سجلات الوصول",
"autoUpdate": "تحديث تلقائي",
"config": "الإعدادات", "config": "الإعدادات",
"backup": "نسخ احتياطي", "backup": "نسخ احتياطي",
"backupTitle": "نسخ احتياطي واستعادة", "backupTitle": "نسخ احتياطي واستعادة",
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "Installation is in progress, please do not refresh this page", "dontRefresh": "Installation is in progress, please do not refresh this page",
"logs": "Logs", "logs": "Logs",
"accessLogs": "Access Logs", "accessLogs": "Access Logs",
"autoUpdate": "Auto Update",
"config": "Config", "config": "Config",
"backup": "Backup", "backup": "Backup",
"backupTitle": "Backup & Restore", "backupTitle": "Backup & Restore",
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "La instalación está en progreso, por favor no actualices esta página.", "dontRefresh": "La instalación está en progreso, por favor no actualices esta página.",
"logs": "Registros", "logs": "Registros",
"accessLogs": "Registros de acceso", "accessLogs": "Registros de acceso",
"autoUpdate": "Actualización automática",
"config": "Configuración", "config": "Configuración",
"backup": "Copia de seguridad", "backup": "Copia de seguridad",
"backupTitle": "Copia & Restauración", "backupTitle": "Copia & Restauración",
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "در حال نصب، لطفا صفحه را رفرش نکنید", "dontRefresh": "در حال نصب، لطفا صفحه را رفرش نکنید",
"logs": "لاگ‌ها", "logs": "لاگ‌ها",
"accessLogs": "لاگ‌های دسترسی", "accessLogs": "لاگ‌های دسترسی",
"autoUpdate": "به‌روزرسانی خودکار",
"config": "پیکربندی", "config": "پیکربندی",
"backup": "پشتیبان‌گیری", "backup": "پشتیبان‌گیری",
"backupTitle": "پشتیبان‌گیری و بازیابی", "backupTitle": "پشتیبان‌گیری و بازیابی",
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "Instalasi sedang berlangsung, harap jangan menyegarkan halaman ini", "dontRefresh": "Instalasi sedang berlangsung, harap jangan menyegarkan halaman ini",
"logs": "Log", "logs": "Log",
"accessLogs": "Log Akses", "accessLogs": "Log Akses",
"autoUpdate": "Pembaruan Otomatis",
"config": "Konfigurasi", "config": "Konfigurasi",
"backup": "Cadangan", "backup": "Cadangan",
"backupTitle": "Cadangan & Pulihkan", "backupTitle": "Cadangan & Pulihkan",
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "インストール中、このページをリロードしないでください", "dontRefresh": "インストール中、このページをリロードしないでください",
"logs": "ログ", "logs": "ログ",
"accessLogs": "アクセスログ", "accessLogs": "アクセスログ",
"autoUpdate": "自動更新",
"config": "設定", "config": "設定",
"backup": "バックアップ", "backup": "バックアップ",
"backupTitle": "バックアップと復元", "backupTitle": "バックアップと復元",
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "Instalação em andamento, por favor não atualize a página", "dontRefresh": "Instalação em andamento, por favor não atualize a página",
"logs": "Logs", "logs": "Logs",
"accessLogs": "Logs de acesso", "accessLogs": "Logs de acesso",
"autoUpdate": "Atualização automática",
"config": "Configuração", "config": "Configuração",
"backup": "Backup", "backup": "Backup",
"backupTitle": "Backup & Restauração", "backupTitle": "Backup & Restauração",
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "Установка в процессе. Не обновляйте страницу", "dontRefresh": "Установка в процессе. Не обновляйте страницу",
"logs": "Логи", "logs": "Логи",
"accessLogs": "Логи доступа", "accessLogs": "Логи доступа",
"autoUpdate": "Автообновление",
"config": "Конфигурация", "config": "Конфигурация",
"backup": "Резервная копия", "backup": "Резервная копия",
"backupTitle": "Бэкап и восстановление", "backupTitle": "Бэкап и восстановление",
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "Kurulum devam ediyor, lütfen bu sayfayı yenilemeyin", "dontRefresh": "Kurulum devam ediyor, lütfen bu sayfayı yenilemeyin",
"logs": "Günlükler", "logs": "Günlükler",
"accessLogs": "Erişim Günlükleri", "accessLogs": "Erişim Günlükleri",
"autoUpdate": "Otomatik Güncelleme",
"config": "Yapılandırma", "config": "Yapılandırma",
"backup": "Yedek", "backup": "Yedek",
"backupTitle": "Yedekleme ve Geri Yükleme", "backupTitle": "Yedekleme ve Geri Yükleme",
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "Інсталяція триває, будь ласка, не оновлюйте цю сторінку", "dontRefresh": "Інсталяція триває, будь ласка, не оновлюйте цю сторінку",
"logs": "Логи", "logs": "Логи",
"accessLogs": "Логи доступу", "accessLogs": "Логи доступу",
"autoUpdate": "Автооновлення",
"config": "Конфігурація", "config": "Конфігурація",
"backup": "Резервна копія", "backup": "Резервна копія",
"backupTitle": "Резервне копіювання та відновлення", "backupTitle": "Резервне копіювання та відновлення",
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "Đang tiến hành cài đặt, vui lòng không làm mới trang này.", "dontRefresh": "Đang tiến hành cài đặt, vui lòng không làm mới trang này.",
"logs": "Nhật ký", "logs": "Nhật ký",
"accessLogs": "Nhật ký truy cập", "accessLogs": "Nhật ký truy cập",
"autoUpdate": "Tự động cập nhật",
"config": "Cấu hình", "config": "Cấu hình",
"backup": "Sao lưu", "backup": "Sao lưu",
"backupTitle": "Sao lưu & Khôi phục", "backupTitle": "Sao lưu & Khôi phục",
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "安装中,请勿刷新此页面", "dontRefresh": "安装中,请勿刷新此页面",
"logs": "日志", "logs": "日志",
"accessLogs": "访问日志", "accessLogs": "访问日志",
"autoUpdate": "自动更新",
"config": "配置", "config": "配置",
"backup": "备份", "backup": "备份",
"backupTitle": "备份和恢复", "backupTitle": "备份和恢复",
+1
View File
@@ -246,6 +246,7 @@
"dontRefresh": "安裝中,請勿重新整理此頁面", "dontRefresh": "安裝中,請勿重新整理此頁面",
"logs": "記錄", "logs": "記錄",
"accessLogs": "存取記錄", "accessLogs": "存取記錄",
"autoUpdate": "自動更新",
"config": "配置", "config": "配置",
"backup": "備份", "backup": "備份",
"backupTitle": "備份和恢復", "backupTitle": "備份和恢復",