Files
OpenFlare/docs/plan/20260719-access-log-ip-tab.md
T
ryan 920a530aa7 feat(access-logs): 新增 IP 明细 Tab 并完善日志详情字段
按时间窗聚合 IP 请求数/2xx 比例/入出站流量,支持排序与详情分析;
日志明细详情仅展示请求业务字段,IP 情报迁至独立详情弹窗。
2026-07-19 00:42:59 +08:00

162 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 访问日志 IP 明细 Tab — 实现计划
## 1. 目标与背景 (Goal & Context)
* **需求背景**:运维需要按 IP 维度快速查看时间窗内的访问量与流量,并下钻单 IP 情报;原先 IP 分析嵌在「单条访问日志详情」中,入口弱、列表能力缺失。
* **开发范围 (Scope) V1**:
* 访问日志页新增第三 Tab **「IP 明细」**(`?tab=ips`)。
* IP 列表:时间筛选(快捷 24h/7d/15d/30d + 自定义 since/until)、分页、按请求数 / 入站 / 出站 / 最后访问 / 2xx 比例排序。
* 列表列:IP、地区、请求数、2xx 比例(2xx 数 / 总请求)、入站流量、出站流量、最后访问。
* 行详情:弹窗展示完整 **IP 情报**(分析 + 趋势 + Top 分布 + 加入 WAF IP 组)。
* **日志明细详情弹窗仅展示单条请求字段**,不再内嵌 IP 情报;如需分析请到 IP 明细。
* **Out of Scope(V1 不做)**:
* 独立 `/access-logs/ip` 子路由全页。
* IP 列表 UI 暴露节点 / host 筛选(后端可保留兼容参数,前端首版不放)。
* 入/出站带宽时间序列(趋势图仍为请求数)。
* 实时 GeoIP 二次查询(沿用入库 `region`)。
## 2. 设计与决策 (Design & Decisions)
### 2.1 页面与交互
| Tab | URL | 内容 |
| --- | --- | --- |
| 概览 | `/access-logs` | 不变 |
| IP 明细 | `/access-logs?tab=ips` | 新 |
| 日志明细 | `/access-logs?tab=list` | 不变;详情弹窗瘦身 |
* **时间筛选(IP 明细)**:
* 快捷:`hours` ∈ {24, 168, 360, 720},默认 168(7d)。
* 自定义:`since` + `until`(RFC3339);**同时提供时覆盖 hours**。
* **详情形态**:留在列表页的 Dialog(非独立子页)。
* **日志详情瘦身**:`access-log-detail-dialog` 只渲染请求字段(时间、节点、IP、地区、host、path、UA、cache、status 等)及必要操作;删除 analysis/trend/WAF 组内嵌区块。WAF「按 IP 加入组」仅保留在 IP 详情弹窗。
### 2.2 API 设计(扩展现有端点,不新建)
**`GET /api/v1/d/access-logs/ip-summary`**
| 参数 | 说明 |
| --- | --- |
| `hours` | 1–720;默认 168;在无 since/until 时生效 |
| `since` / `until` | 可选 RFC3339;同时有效时优先于 hours |
| `sort_by` | `total_requests`(默认)\| `request_length`(入站)\| `bytes_sent`(出站)\| `last_seen_at` \| `success_ratio` |
| `sort_order` | `asc` \| `desc` |
| `p` / `page_size` | 分页,page_size 上限 200 |
| `remote_addr` / `node_id` / `host` | 兼容保留;V1 UI 可不暴露 |
**响应行字段(扩展)**
```text
remote_addr string
region string // 窗内 argMax(region, logged_at) 或等价
total_requests uint64
success_2xx_count uint64 // status_code 200–299
success_ratio float64 // success_2xx_count / total_requests;total=0 时为 0
bytes_received uint64 // sum(request_length) 入站
bytes_sent uint64 // sum(bytes_sent) 出站
last_seen_at time
```
* `recent_requests`:可停止计算或固定返回 0;**UI 不展示**。避免与可配置时间窗语义冲突。
* 详情下钻仍用现有:
* `GET .../ip-summary/analysis?remote_addr=&hours=`(或 since/until,若后续扩展;V1 将列表当前窗映射为 hours 或 since/until 与后端约定一致)
* `GET .../ip-summary/trend?remote_addr=&hours=&bucket_minutes=`
**分析/趋势时间窗对齐**:打开 IP 详情时,将列表当前时间窗传入 analysis/trend(优先 since/until;仅有 hours 则传 hours)。
### 2.3 数据层(ClickHouse)
* 表:`of_node_access_logs`(已有 `bytes_sent`、`request_length`、`status_code`、`region`)。
* 聚合:`GROUP BY remote_addr`,在 `NodeAccessLogFilter.Since/Until` 上过滤。
* 2xx:`countIf(status_code >= 200 AND status_code < 300)`。
* region:`argMax(region, logged_at)`。
* 排序:服务端 ORDER BY 对应表达式;`success_ratio` 注意除零(`if(total=0,0,ratio)`)。
### 2.4 设计决策权衡
| 选项 | 结论 |
| --- | --- |
| 扩展 `/ip-summary` vs 新 `/ip-list` | **扩展现有**,前端 service 已有 `listIPSummaries` |
| 详情弹窗 vs 子页 | **弹窗**,与现有明细交互一致 |
| IP 情报放日志详情 vs 独立 IP 详情 | **仅 IP 明细详情**;日志详情只展示请求信息 |
| 时间:仅快捷 vs 仅自定义 | **两者都要**,自定义优先 |
### 2.5 数据流(示意)
```mermaid
flowchart LR
UI_IP[IP 明细 Tab] --> API_List[GET /ip-summary]
API_List --> CH[(of_node_access_logs)]
UI_IP --> UI_Dlg[IP 详情 Dialog]
UI_Dlg --> API_A[GET /ip-summary/analysis]
UI_Dlg --> API_T[GET /ip-summary/trend]
API_A --> CH
API_T --> CH
UI_List[日志明细 Tab] --> API_Logs[GET /access-logs]
UI_List --> UI_LogDlg[日志详情 Dialog]
UI_LogDlg -.->|不请求 IP 分析| X[仅请求字段]
```
## 3. 具体修改文件清单 (Proposed Changes)
### 后端 Server
* #### [MODIFY] `internal/repository/analytics/node_access_log_stats.go`(及 filter 如有)
* `IPSummariesNodeAccessLogs`:时间窗、sum 入/出、2xx count、ratio、region、扩展 sort。
* #### [MODIFY] `internal/apps/openflare/observability/access_log_logics.go`
* Query/View 类型扩展;解析 hours/since/until;去掉或忽略 recent 3h 硬编码。
* #### [MODIFY] `internal/apps/openflare/observability/routers.go` / handler
* 绑定新 query;Swagger 注释。
* #### [MODIFY] 相关单元测试(logics / repository 若有)
### 前端 Web
* #### [MODIFY] `frontend/app/(main)/access-logs/page.tsx`
* 第三 Tab `ips`;`resolveTab` / `handleTabChange`。
* #### [NEW] `frontend/app/(main)/access-logs/components/ip-tab.tsx`
* 列表、时间筛选、排序、分页、打开详情。
* #### [NEW] `frontend/app/(main)/access-logs/components/ip-detail-dialog.tsx`
* IP 入口详情壳。
* #### [NEW] `frontend/app/(main)/access-logs/components/ip-analysis-panel.tsx`
* 从现有 `access-log-detail-dialog` **迁出** 分析/趋势/排行/WAF IP 组逻辑。
* #### [MODIFY] `frontend/app/(main)/access-logs/components/access-log-detail-dialog.tsx`
* **删除** IP 情报相关 UI 与 `getIPAnalysis` / `getIPTrend` 请求;仅请求日志字段展示。
* #### [MODIFY] `frontend/app/(main)/access-logs/components/access-log-utils.ts`
* tab 类型、IP 排序选项、时间筛选辅助。
* #### [MODIFY] `frontend/lib/services/openflare/access-log.service.ts` + `types.ts`
* `listIPSummaries` 参数与 `AccessLogIPSummaryItem` 字段同步。
### 文档
* #### [MODIFY] `docs/changelog/index.md` — `[Unreleased]` 用户可见说明
* #### [MODIFY] `docs/design/observability-design.md` 或 data-model(如有访问日志 UI 约定)— 补充 IP 明细 Tab 与 API 字段(中文)
* #### [MODIFY] `docs/plan/index.md` — 挂上本计划链接
## 4. 验证计划 (Verification Plan)
### 自动化
```bash
go test ./internal/apps/openflare/observability/ ./internal/repository/analytics/
# 前端:相关 tsc / 页面无类型错误
make code-check # 完成后按项目门禁
make prettier
make swagger # API 注释变更后
```
### 手动
1. `/access-logs?tab=ips` 默认 7d 列表有数据;切换 24h/自定义区间结果变化。
2. 分别按请求数、入站、出站、2xx 比例、最后访问排序正确。
3. 2xx 比例 = 2xx/总数;0 请求不出现 NaN/Infinity。
4. 点 IP 打开详情:指标/趋势/Top/WAF 组可用;时间窗与列表一致。
5. 日志明细 → 详情:仅请求信息,**无** IP 分析/趋势区块。
6. 概览 Tab 行为无回归。
## 5. 状态
- [x] 需求澄清与方案确认
- [x] 实现(后端 ip-summary 扩展 + 前端 IP 明细 Tab + 日志详情瘦身)
- [x] 测试与 changelog(`go test` 相关包通过;changelog 已更新)
- [ ] 提交合并