diff --git a/docs/changelog/index.md b/docs/changelog/index.md index 50842ade..e7599e98 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -19,7 +19,7 @@ sidebar: false ## [Unreleased] ### 新增 -- 访问日志「日志明细」支持按 HTTP 状态码筛选:状态码下拉含常用 2xx/3xx/4xx/5xx 快捷选项,也可手动输入任意状态码,筛选条件贯穿 ClickHouse 与 PostgreSQL/SQLite 日志库。 +- 访问日志「日志明细」支持按 HTTP 状态码筛选:直接输入任意状态码(100-599),筛选条件贯穿 ClickHouse 与 PostgreSQL/SQLite 日志库。 - 访问日志「日志明细」支持自定义时间范围筛选:内置 shadcn 日期+时间选择器,可指定起止时间(RFC3339,需成对提供)。 - 首页看板改版:24 小时请求趋势拆分展示请求总量与 2xx/4xx/5xx 状态码类请求量并独占一行;移除宿主机磁盘指标,24 小时容量趋势(CPU/内存)并入业务流量卡片展示。 diff --git a/frontend/app/(main)/access-logs/components/access-log-filters.tsx b/frontend/app/(main)/access-logs/components/access-log-filters.tsx index 82b47437..1167072b 100644 --- a/frontend/app/(main)/access-logs/components/access-log-filters.tsx +++ b/frontend/app/(main)/access-logs/components/access-log-filters.tsx @@ -25,7 +25,7 @@ import { SelectValue, } from '@/components/ui/select'; import type { SearchDraft } from './access-log-utils'; -import { PAGE_SIZE_OPTIONS, STATUS_CODE_OPTIONS } from './access-log-utils'; +import { PAGE_SIZE_OPTIONS } from './access-log-utils'; interface AccessLogFiltersProps { draft: SearchDraft; @@ -36,7 +36,6 @@ interface AccessLogFiltersProps { onReset: () => void; } -const CUSTOM_STATUS_CODE = '__custom__'; const HOUR_OPTIONS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0'), ); @@ -127,11 +126,7 @@ function DateTimePicker({ - +
option.value === draft.statusCode, - ); - const statusSelectValue = - draft.statusCode === '' - ? '' - : isPresetStatus - ? draft.statusCode - : CUSTOM_STATUS_CODE; return (
@@ -188,9 +174,7 @@ export function AccessLogFilters({ - onDraftChange({ ...draft, host: e.target.value }) - } + onChange={(e) => onDraftChange({ ...draft, host: e.target.value })} onKeyDown={(e) => { if (e.key === 'Enter') onSearch(); }} @@ -199,50 +183,20 @@ export function AccessLogFilters({ /> -
- - {statusSelectValue === CUSTOM_STATUS_CODE ? ( - - onDraftChange({ - ...draft, - statusCode: e.target.value.replace(/\D/g, '').slice(0, 3), - }) - } - onKeyDown={(e) => { - if (e.key === 'Enter') onSearch(); - }} - placeholder='输入状态码,如 201' - autoFocus - className='h-9 text-xs' - /> - ) : null} -
+ + onDraftChange({ + ...draft, + statusCode: e.target.value.replace(/\D/g, '').slice(0, 3), + }) + } + onKeyDown={(e) => { + if (e.key === 'Enter') onSearch(); + }} + placeholder='输入状态码,如 404' + className='h-9 text-xs' + />
diff --git a/frontend/app/(main)/access-logs/components/access-log-utils.ts b/frontend/app/(main)/access-logs/components/access-log-utils.ts index fc85dc10..70e93343 100644 --- a/frontend/app/(main)/access-logs/components/access-log-utils.ts +++ b/frontend/app/(main)/access-logs/components/access-log-utils.ts @@ -17,23 +17,6 @@ export type RateLimitRangeHours = 24 | 72; export const PAGE_SIZE_OPTIONS = [20, 50, 100, 200]; -/** 状态码快捷筛选项(空值表示全部,由 Select placeholder 兜底)。 */ -export const STATUS_CODE_OPTIONS: { value: string; label: string }[] = [ - { value: '200', label: '200 OK' }, - { value: '301', label: '301 永久重定向' }, - { value: '302', label: '302 临时重定向' }, - { value: '304', label: '304 未修改' }, - { value: '400', label: '400 请求错误' }, - { value: '401', label: '401 未授权' }, - { value: '403', label: '403 禁止访问' }, - { value: '404', label: '404 未找到' }, - { value: '429', label: '429 请求过多' }, - { value: '500', label: '500 服务器错误' }, - { value: '502', label: '502 网关错误' }, - { value: '503', label: '503 服务不可用' }, - { value: '504', label: '504 网关超时' }, -]; - export const OVERVIEW_RANGE_OPTIONS: { value: OverviewRangeHours; label: string; diff --git a/frontend/app/(main)/components/dashboard/geo-distribution-list.tsx b/frontend/app/(main)/components/dashboard/geo-distribution-list.tsx deleted file mode 100644 index 43a6f2c8..00000000 --- a/frontend/app/(main)/components/dashboard/geo-distribution-list.tsx +++ /dev/null @@ -1,60 +0,0 @@ -'use client'; - -import { Globe2 } from 'lucide-react'; - -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from '@/components/ui/card'; -import { Progress } from '@/components/ui/progress'; -import type { DistributionItem } from '@/lib/services/openflare'; - -import { formatCompactNumber } from './dashboard-utils'; - -export function GeoDistributionList({ items }: { items: DistributionItem[] }) { - const sortedItems = [...items] - .sort((left, right) => right.value - left.value) - .slice(0, 8); - const maxValue = sortedItems[0]?.value ?? 0; - - return ( - - - - - 来源国家分布 - - - 聚合最近 24 小时主要来源国家。 - - - - {sortedItems.length === 0 ? ( -
- 暂无来源分布数据 -
- ) : ( -
- {sortedItems.map((item) => { - const ratio = maxValue > 0 ? (item.value / maxValue) * 100 : 0; - return ( -
-
- {item.key || '未知'} - - {formatCompactNumber(item.value)} - -
- -
- ); - })} -
- )} -
-
- ); -} diff --git a/frontend/app/(main)/components/dashboard/traffic-capacity-trend-chart.tsx b/frontend/app/(main)/components/dashboard/traffic-capacity-trend-chart.tsx index 25f0f394..6044a5eb 100644 --- a/frontend/app/(main)/components/dashboard/traffic-capacity-trend-chart.tsx +++ b/frontend/app/(main)/components/dashboard/traffic-capacity-trend-chart.tsx @@ -7,11 +7,7 @@ import type { NetworkTrendPoint, } from '@/lib/services/openflare'; -import { - formatBytes, - formatPercent, - formatTrendHour, -} from './dashboard-utils'; +import { formatBytes, formatPercent, formatTrendHour } from './dashboard-utils'; /** 业务流量(来自访问日志)与容量趋势(节点 Agent 宿主机指标)合并展示。 */ export function TrafficCapacityTrendChart({ diff --git a/frontend/app/(main)/page.tsx b/frontend/app/(main)/page.tsx index eab2bca0..901c0faf 100644 --- a/frontend/app/(main)/page.tsx +++ b/frontend/app/(main)/page.tsx @@ -1,112 +1,112 @@ 'use client'; -import {useQuery} from '@tanstack/react-query'; -import {LayoutDashboard, RefreshCw} from 'lucide-react'; +import { useQuery } from '@tanstack/react-query'; +import { LayoutDashboard, RefreshCw } from 'lucide-react'; -import {EmptyStateWithBorder} from '@/components/layout/empty'; -import {ErrorInline} from '@/components/layout/error'; -import {LoadingStateWithBorder} from '@/components/layout/loading'; -import {Button} from '@/components/ui/button'; -import {DashboardService} from '@/lib/services/openflare'; -import {formatDateTime} from '@/lib/utils'; +import { EmptyStateWithBorder } from '@/components/layout/empty'; +import { ErrorInline } from '@/components/layout/error'; +import { LoadingStateWithBorder } from '@/components/layout/loading'; +import { Button } from '@/components/ui/button'; +import { DashboardService } from '@/lib/services/openflare'; +import { formatDateTime } from '@/lib/utils'; import { - SourceDistributionChart, - StatusCodeDistributionChart, - TopDomainChart, + SourceDistributionChart, + StatusCodeDistributionChart, + TopDomainChart, } from './components/dashboard/distribution-rank-charts'; -import {NodeHealthTable} from './components/dashboard/node-health-table'; -import {TrafficCapacityTrendChart} from './components/dashboard/traffic-capacity-trend-chart'; -import {TrafficTrendChart} from './components/dashboard/traffic-trend-chart'; -import {WorldStage} from './components/dashboard/world-stage'; -import {getErrorMessage} from './nodes/components/node-utils'; +import { NodeHealthTable } from './components/dashboard/node-health-table'; +import { TrafficCapacityTrendChart } from './components/dashboard/traffic-capacity-trend-chart'; +import { TrafficTrendChart } from './components/dashboard/traffic-trend-chart'; +import { WorldStage } from './components/dashboard/world-stage'; +import { getErrorMessage } from './nodes/components/node-utils'; const dashboardQueryKey = ['openflare', 'dashboard', 'overview']; export default function OpenFlareDashboardPage() { - const overviewQuery = useQuery({ - queryKey: dashboardQueryKey, - queryFn: () => DashboardService.getOverview(), - refetchInterval: 60_000, - }); + const overviewQuery = useQuery({ + queryKey: dashboardQueryKey, + queryFn: () => DashboardService.getOverview(), + refetchInterval: 60_000, + }); - const overview = overviewQuery.data; + const overview = overviewQuery.data; - return ( -
-
-
- -

总览

-
-
- {overview?.generated_at ? ( - 数据生成于 {formatDateTime(overview.generated_at)} - ) : null} - -
-
- - {overviewQuery.isLoading ? ( - - ) : overviewQuery.isError ? ( - overviewQuery.refetch()} - /> - ) : !overview ? ( - - ) : ( - <> - - -
- -
- -
- - - -
- -
- -
- - - - )} + return ( +
+
+
+ +

总览

- ); +
+ {overview?.generated_at ? ( + 数据生成于 {formatDateTime(overview.generated_at)} + ) : null} + +
+
+ + {overviewQuery.isLoading ? ( + + ) : overviewQuery.isError ? ( + overviewQuery.refetch()} + /> + ) : !overview ? ( + + ) : ( + <> + + +
+ +
+ +
+ + + +
+ +
+ +
+ + + + )} +
+ ); } diff --git a/internal/apps/openflare/observability/routers.go b/internal/apps/openflare/observability/routers.go index af1f61de..673d1205 100644 --- a/internal/apps/openflare/observability/routers.go +++ b/internal/apps/openflare/observability/routers.go @@ -5,11 +5,12 @@ package observability import ( "errors" + "net/http" + "strconv" + "github.com/Rain-kl/Wavelet/internal/apps/openflare/apiutil" "github.com/Rain-kl/Wavelet/internal/shared/response" "github.com/gin-gonic/gin" - "net/http" - "strconv" ) // GetAccessLogOverviewHandler 获取访问日志概览。