diff --git a/frontend/src/app/dashboard/logs/page.tsx b/frontend/src/app/dashboard/logs/page.tsx new file mode 100644 index 0000000..fd0f828 --- /dev/null +++ b/frontend/src/app/dashboard/logs/page.tsx @@ -0,0 +1,401 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import axios from 'axios'; +import { Loader2, RefreshCw, Search, Filter, Clock, AlertCircle, AlertTriangle, Info, Bug } from 'lucide-react'; + +interface Application { + id: string; + name: string; + createdAt: string; +} + +interface LogFilter { + appId: string; + level: string; + from: string; + to: string; + search: string; +} + +interface LogEntry { + timestamp: string; + level: string; + message: string; + app?: string; + pod?: string; +} + +const LOG_LEVELS = [ + { value: '', label: 'همه سطوح', color: 'bg-gray-100' }, + { value: 'error', label: 'Error', color: 'bg-red-100 text-red-800' }, + { value: 'warn', label: 'Warning', color: 'bg-yellow-100 text-yellow-800' }, + { value: 'info', label: 'Info', color: 'bg-blue-100 text-blue-800' }, + { value: 'debug', label: 'Debug', color: 'bg-gray-100 text-gray-800' }, +]; + +const TIME_RANGES = [ + { value: '1h', label: 'ساعت گذشته' }, + { value: '6h', label: '6 ساعت گذشته' }, + { value: '24h', label: '24 ساعت گذشته' }, + { value: '7d', label: 'هفته گذشته' }, + { value: 'custom', label: 'بازه دلخواه' }, +]; + +export default function LogsPage() { + const [filters, setFilters] = useState({ + appId: '', + level: '', + from: '', + to: '', + search: '', + }); + const [timeRange, setTimeRange] = useState('24h'); + const [autoRefresh, setAutoRefresh] = useState(false); + + // Fetch user's applications + const { data: applications } = useQuery({ + queryKey: ['applications'], + queryFn: async () => { + const { data } = await axios.get('/api/applications'); + return data; + }, + }); + + // Fetch logs query + const { data: logsResult, isLoading, refetch, isFetching } = useQuery({ + queryKey: ['logs', filters, timeRange], + queryFn: async () => { + const params = new URLSearchParams(); + if (filters.appId) params.append('appId', filters.appId); + if (filters.level) params.append('level', filters.level); + if (filters.search) params.append('search', filters.search); + + if (timeRange !== 'custom') { + const now = new Date(); + const from = new Date(); + switch (timeRange) { + case '1h': from.setHours(now.getHours() - 1); break; + case '6h': from.setHours(now.getHours() - 6); break; + case '24h': from.setDate(now.getDate() - 1); break; + case '7d': from.setDate(now.getDate() - 7); break; + } + params.append('from', from.toISOString()); + } else { + if (filters.from) params.append('from', filters.from); + if (filters.to) params.append('to', filters.to); + } + + const { data } = await axios.get(`/api/logs?${params.toString()}`); + return data; + }, + refetchInterval: autoRefresh ? 5000 : false, + }); + + // Fetch log stats + const { data: logStats } = useQuery({ + queryKey: ['logStats', filters.appId, timeRange], + queryFn: async () => { + const params = new URLSearchParams(); + if (filters.appId) params.append('appId', filters.appId); + params.append('period', timeRange === 'custom' ? '24h' : timeRange); + + const { data } = await axios.get(`/api/logs/stats?${params.toString()}`); + return data; + }, + }); + + // Fetch recent errors + const { data: recentErrors } = useQuery({ + queryKey: ['recentErrors', filters.appId], + queryFn: async () => { + const params = new URLSearchParams(); + if (filters.appId) params.append('appId', filters.appId); + params.append('limit', '10'); + + const { data } = await axios.get(`/api/logs/errors?${params.toString()}`); + return data; + }, + }); + + const getLevelBadgeClass = (level: string) => { + const levelItem = LOG_LEVELS.find(l => l.value === level.toLowerCase()); + return levelItem?.color || 'bg-gray-100'; + }; + + return ( +
+
+

لاگ‌های اپلیکیشن

+

+ مشاهده و جستجو در لاگ‌های اپلیکیشن‌های خود +

+
+ + {/* Filters */} +
+
+ {/* App Filter */} +
+ + +
+ + {/* Level Filter */} +
+ + +
+ + {/* Time Range */} +
+ + +
+ + {/* Search */} +
+ + setFilters({ ...filters, search: e.target.value })} + placeholder="جستجو..." + className="w-full rounded-lg border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500" + /> +
+
+ + {/* Custom date range */} + {timeRange === 'custom' && ( +
+
+ + setFilters({ ...filters, from: e.target.value })} + className="w-full rounded-lg border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500" + /> +
+
+ + setFilters({ ...filters, to: e.target.value })} + className="w-full rounded-lg border-gray-300 shadow-sm focus:ring-indigo-500 focus:border-indigo-500" + /> +
+
+ )} + + {/* Action buttons */} +
+ + + +
+
+ + {/* Stats Overview */} + {logStats && ( +
+
+
کل لاگ‌ها
+
+ {logStats.meta?.period || '-'} +
+
+
+
خطاها
+
+ {recentErrors?.meta?.limit || 0} +
+
+
+
هشدارها
+
-
+
+
+
اپلیکیشن فعال
+
+ {filters.appId + ? applications?.find(a => a.id === filters.appId)?.name || '-' + : `${applications?.length || 0} اپلیکیشن`} +
+
+
+ )} + + {/* Query Info */} + {logsResult && ( +
+

اطلاعات کوئری

+

{logsResult.usage?.description}

+
+
{JSON.stringify(logsResult.query, null, 2)}
+
+

+ 💡 این کوئری را می‌توانید در Elasticsearch یا Kibana اجرا کنید. +

+
+ )} + + {/* Logs Table */} +
+
+

لاگ‌ها

+ {isFetching && ( + + + در حال بارگذاری... + + )} +
+ + {isLoading ? ( +
+ در حال بارگذاری لاگ‌ها... +
+ ) : ( +
+ + + + + + + + + + + {/* Sample rows - in production, this would come from actual ES query results */} + + + + + + + +
+ زمان + + سطح + + اپلیکیشن + + پیام +
+ {new Date().toISOString().slice(0, 19).replace('T', ' ')} + + + INFO + + + نمونه اپلیکیشن + + برای مشاهده لاگ‌ها، کوئری بالا را در Elasticsearch اجرا کنید +
+
+ )} + + {/* Pagination info */} + {logsResult?.meta && ( +
+ صفحه {logsResult.meta.page} | {logsResult.meta.limit} آیتم در هر صفحه +
+ )} +
+ + {/* Recent Errors */} + {recentErrors && ( +
+
+

آخرین خطاها

+
+
+

+ برای مشاهده خطاهای اخیر، کوئری زیر را در Elasticsearch اجرا کنید: +

+
+
{JSON.stringify(recentErrors.query, null, 2)}
+
+
+
+ )} + + {/* Kibana Link */} +
+

🔍 مشاهده در Kibana

+

+ برای تجربه بهتر در مشاهده و آنالیز لاگ‌ها، از Kibana استفاده کنید. +

+
+ kubectl port-forward svc/kibana 5601:5601 -n logging +
+

+ سپس به آدرس http://localhost:5601 بروید +

+
+
+ ); +}