'use client'; import { getSystemLog, getVersion, restartSystem } from '@/services/admin/tool'; import { useQuery } from '@tanstack/react-query'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from '@workspace/ui/components/accordion'; import { AlertDialog, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from '@workspace/ui/components/alert-dialog'; import { Badge } from '@workspace/ui/components/badge'; import { Button } from '@workspace/ui/components/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@workspace/ui/components/card'; import { ScrollArea } from '@workspace/ui/components/scroll-area'; import { Icon } from '@workspace/ui/custom-components/icon'; import { formatDate } from '@workspace/ui/utils'; import { useTranslations } from 'next-intl'; import { useState } from 'react'; import packageJson from '../../../../../package.json'; const getLogLevelColor = (level: string) => { const colorMap: { [key: string]: string } = { INFO: 'bg-blue-100 text-blue-800 hover:bg-blue-200', WARN: 'bg-yellow-100 text-yellow-800 hover:bg-yellow-200', ERROR: 'bg-red-100 text-red-800 hover:bg-red-200', }; return colorMap[level] || 'bg-gray-100 text-gray-800 hover:bg-gray-200'; }; export default function Page() { const t = useTranslations('tool'); const { data: logs, refetch, isLoading, } = useQuery({ queryKey: ['getSystemLog'], queryFn: async () => { const { data } = await getSystemLog(); return data.data?.list || []; }, }); const [openRestart, setOpenRestart] = useState(false); const [isRestarting, setIsRestarting] = useState(false); const { data: latestReleases } = useQuery({ queryKey: ['getLatestReleases'], queryFn: async () => { try { const [webResponse, serverResponse] = await Promise.all([ fetch('https://api.github.com/repos/perfect-panel/ppanel-web/releases/latest'), fetch('https://api.github.com/repos/perfect-panel/server/releases/latest'), ]); const webData = webResponse.ok ? await webResponse.json() : null; const serverData = serverResponse.ok ? await serverResponse.json() : null; return { web: webData ? { version: webData.tag_name, url: webData.html_url, publishedAt: webData.published_at, } : null, server: serverData ? { version: serverData.tag_name, url: serverData.html_url, publishedAt: serverData.published_at, } : null, }; } catch (error) { console.error('Failed to fetch latest releases:', error); return { web: null, server: null }; } }, staleTime: 60 * 60 * 1000, retry: 1, retryDelay: 10000, }); // 检查是否有新版本 const hasNewVersion = latestReleases?.web && packageJson.version !== latestReleases.web.version.replace(/^v/, ''); const { data: systemInfo } = useQuery({ queryKey: ['getVersion'], queryFn: async () => { const { data } = await getVersion(); const versionString = data.data?.version || ''; const releaseVersionRegex = /^[Vv]?\d+\.\d+\.\d+$/; const timeMatch = versionString.match(/\(([^)]+)\)/); const timeInBrackets = timeMatch ? timeMatch[1] : ''; const versionWithoutTime = versionString.replace(/\([^)]*\)/, '').trim(); const isDevelopment = !releaseVersionRegex.test(versionWithoutTime); let baseVersion = versionWithoutTime; let versionSuffix = ''; let lastUpdated = ''; if (isDevelopment && versionWithoutTime.includes('-')) { const parts = versionWithoutTime.split('-'); baseVersion = parts[0] || versionWithoutTime; versionSuffix = parts.slice(1).join('-'); } lastUpdated = formatDate(new Date(timeInBrackets || Date.now())) || ''; const displayVersion = baseVersion.startsWith('V') || baseVersion.startsWith('v') ? baseVersion : `V${baseVersion}`; return { isRelease: !isDevelopment, version: displayVersion, lastUpdated, }; }, }); const hasServerNewVersion = latestReleases?.server && systemInfo && systemInfo.version.replace(/^V/, '') !== latestReleases.server.version.replace(/^v/, ''); return (
{t('systemServices')} {t('viewLogsAndManage')}
{/* {t('confirmSystemUpgrade')} {t('upgradeDescription')} {t('cancel')} {t('confirmUpgrade')} */} {t('confirmSystemReboot')} {t('rebootDescription')} {t('cancel')}
{/* 版本信息紧凑显示 */}
{/* 用户端/管理端版本 */}
{t('webVersion')} V{packageJson.version} {hasNewVersion && ( {t('newVersionAvailable')} )}
{hasNewVersion && ( )}
{/* 服务端版本 */}
{t('serverVersion')} {systemInfo?.version || 'V1.0.0'} {!systemInfo?.isRelease && ( {t('developmentVersion')} )} {hasServerNewVersion && ( {t('newVersionAvailable')} )}
{hasServerNewVersion && ( )}
{systemInfo?.lastUpdated || '--'}
{t('systemLogs')}
{isLoading ? (
) : ( {logs?.map((log: any, index: number) => (
{log.timestamp}
{Object.entries(log).map(([key, value]) => (
{key}: {value as string}
))} {/*
{t('ip')}:
{log.ip}
{t('request')}:
{log.request}
{t('status')}:
{log.status}
{t('caller')}:
{log.caller}
{t('errors')}:
{log.errors || t('none')}
{t('query')}:
{log.query || t('none')}
{t('userAgent')}:
{log['user-agent']}
*/}
))}
)}
); }