From 04f029c705a7d2c01b0dc8e5a4db1cf81c7f620b Mon Sep 17 00:00:00 2001 From: ryan Date: Sat, 18 Jul 2026 23:32:49 +0800 Subject: [PATCH] =?UTF-8?q?feat(cache):=20=E5=BC=80=E5=90=AF=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E9=BB=98=E8=AE=A4=E4=BB=85=E7=BC=93=E5=AD=98=E6=A0=87?= =?UTF-8?q?=E5=87=86=E9=9D=99=E6=80=81=E8=B5=84=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 路由缓存策略新增 static(内置扩展名,不含 HTML)与 all; 存量 url 规范为 all。OpenResty 渲染与代理路由 UI 同步。 --- docs/changelog/index.md | 1 + .../20260718-edge-cache-static-default.md | 22 ++++++ docs/plan/index.md | 1 + .../(main)/proxy-routes/components/helpers.ts | 11 ++- .../components/proxy-route-create-sheet.tsx | 2 +- .../detail/components/cache-section.tsx | 71 ++++++++++++++----- .../apps/openflare/proxy_route/helpers.go | 21 ++++-- .../apps/openflare/proxy_route/logics_test.go | 20 ++++++ pkg/render/openresty/render.go | 24 ++++++- pkg/render/openresty/render_test.go | 43 +++++++++++ pkg/render/openresty/types.go | 14 ++++ 11 files changed, 201 insertions(+), 29 deletions(-) create mode 100644 docs/plan/20260718-edge-cache-static-default.md diff --git a/docs/changelog/index.md b/docs/changelog/index.md index f7f77ef4..2cca0690 100644 --- a/docs/changelog/index.md +++ b/docs/changelog/index.md @@ -23,6 +23,7 @@ sidebar: false ### 变更 +- 边缘缓存默认策略调整为「标准静态资源」:开启站点缓存后默认仅缓存 css/js/图片/字体等扩展名(不含 HTML);原「按 URL」行为保留为「所有可缓存 GET」。 - 优化访问日志概览及其他图表全局排行榜 (RankChart) 的样式布局:将每一项改为单行横向排布(左侧标签、中间进度条、右侧数值),数值支持自动格式化为 Compact 形式(如 39.57k、1.2M),同时调整默认高度为 320px 并隐藏滚动条。 - 访问日志页面重构为「概览」与「日志明细」两个标签页:概览展示请求量、访问量、带宽趋势与 Top Paths/Hosts/IPs,明细列表保留检索能力;已移除时间折叠与 IP 汇总视图。 - 边缘访问日志现支持上报并存储 User-Agent;概览新增设备类型饼图,以及浏览器、操作系统、User-Agent 排行。 diff --git a/docs/plan/20260718-edge-cache-static-default.md b/docs/plan/20260718-edge-cache-static-default.md new file mode 100644 index 00000000..d0d0c386 --- /dev/null +++ b/docs/plan/20260718-edge-cache-static-default.md @@ -0,0 +1,22 @@ +# 边缘缓存默认 static 策略 — 实现计划 + +对应设计:[edge-cache-design.md](../design/edge-cache-design.md) + +## 目标 + +路由开启缓存后默认仅缓存标准静态扩展名(`static`);存量 `url` 映射为 `all`。 + +## 修改清单 + +1. **渲染** `pkg/render/openresty/render.go`:`static` 内置扩展名;`url`/`all` 无路径限制 +2. **校验** `internal/apps/openflare/proxy_route/helpers.go`:策略枚举与规范化 +3. **前端** `cache-section.tsx` + helpers:默认 `static`,选项文案 +4. **测试** render + helpers +5. **changelog** + +## 验证 + +```bash +go test ./pkg/render/openresty/ ./internal/apps/openflare/proxy_route/ +make code-check # 或至少 go test + frontend tsc +``` diff --git a/docs/plan/index.md b/docs/plan/index.md index 7ecb8bda..3066f7b7 100644 --- a/docs/plan/index.md +++ b/docs/plan/index.md @@ -17,6 +17,7 @@ * [WAF 可编排规则](./20260713-waf-orchestration.md):使用 React Flow 编辑 DAG 规则,发布时编译并由 OpenResty 纯内存执行。 * [边缘可观测与业务流量统计重构](./20260717-observability-redesign.md):访问日志为业务唯一真相;Agent 只上报明细与主机读数;收敛「出站/已提供」双字段。 * [访问日志 cache_status 明细可见](./20260718-access-log-cache-status.md):上报 `$upstream_cache_status`,明细展示命中/回源/未缓存三态。 +* [边缘缓存默认 static 策略](./20260718-edge-cache-static-default.md):开启缓存默认仅静态扩展名;存量 url→all。 ## 使用建议 diff --git a/frontend/app/(main)/proxy-routes/components/helpers.ts b/frontend/app/(main)/proxy-routes/components/helpers.ts index 20e35482..3cee39b2 100644 --- a/frontend/app/(main)/proxy-routes/components/helpers.ts +++ b/frontend/app/(main)/proxy-routes/components/helpers.ts @@ -278,10 +278,10 @@ export function normalizeLimitRate(value: string) { } export function validateCacheRules( - policy: 'url' | 'suffix' | 'path_prefix' | 'path_exact', + policy: 'static' | 'all' | 'url' | 'suffix' | 'path_prefix' | 'path_exact', rules: string[], ) { - if (policy === 'url') { + if (policy === 'static' || policy === 'all' || policy === 'url') { return null; } @@ -335,7 +335,12 @@ export function buildPayloadFromRoute( limit_conn_per_ip: route.limit_conn_per_ip, limit_rate: route.limit_rate, cache_enabled: route.cache_enabled, - cache_policy: route.cache_policy || 'url', + cache_policy: + !route.cache_policy || route.cache_policy === 'url' + ? route.cache_policy === 'url' + ? 'all' + : 'static' + : route.cache_policy, cache_rules: route.cache_rule_list ?? [], custom_headers: route.custom_header_list ?? [], basic_auth_enabled: route.basic_auth_enabled, diff --git a/frontend/app/(main)/proxy-routes/components/proxy-route-create-sheet.tsx b/frontend/app/(main)/proxy-routes/components/proxy-route-create-sheet.tsx index 156058d3..26afb5ae 100644 --- a/frontend/app/(main)/proxy-routes/components/proxy-route-create-sheet.tsx +++ b/frontend/app/(main)/proxy-routes/components/proxy-route-create-sheet.tsx @@ -229,7 +229,7 @@ export function ProxyRouteCreateSheet({ limit_conn_per_ip: 0, limit_rate: '', cache_enabled: false, - cache_policy: 'url', + cache_policy: 'static', cache_rules: [], custom_headers: [], basic_auth_enabled: false, diff --git a/frontend/app/(main)/proxy-routes/detail/components/cache-section.tsx b/frontend/app/(main)/proxy-routes/detail/components/cache-section.tsx index e0983eac..fd22f82f 100644 --- a/frontend/app/(main)/proxy-routes/detail/components/cache-section.tsx +++ b/frontend/app/(main)/proxy-routes/detail/components/cache-section.tsx @@ -36,7 +36,14 @@ import { SectionShell } from './section-shell'; const cacheSchema = z .object({ cache_enabled: z.boolean(), - cache_policy: z.enum(['url', 'suffix', 'path_prefix', 'path_exact']), + cache_policy: z.enum([ + 'static', + 'all', + 'url', + 'suffix', + 'path_prefix', + 'path_exact', + ]), cache_rules_text: z.string(), }) .superRefine((value, context) => { @@ -63,6 +70,22 @@ interface CacheSectionProps { onSavingChange?: (saving: boolean) => void; } +function normalizeCachePolicyValue(policy: string | undefined | null) { + const value = (policy || '').trim(); + if (!value || value === 'static') return 'static'; + if (value === 'url' || value === 'all') return 'all'; + if (value === 'suffix' || value === 'path_prefix' || value === 'path_exact') { + return value; + } + return 'static'; +} + +function needsRulesForPolicy(policy: string) { + return ( + policy === 'suffix' || policy === 'path_prefix' || policy === 'path_exact' + ); +} + export function CacheSection({ route, onRouteUpdate, @@ -78,8 +101,9 @@ export function CacheSection({ resolver: zodResolver(cacheSchema), defaultValues: { cache_enabled: route.cache_enabled, - cache_policy: (route.cache_policy || - 'url') as CacheValues['cache_policy'], + cache_policy: normalizeCachePolicyValue( + route.cache_policy, + ) as CacheValues['cache_policy'], cache_rules_text: route.cache_rule_list.join('\n'), }, }); @@ -87,14 +111,19 @@ export function CacheSection({ useEffect(() => { form.reset({ cache_enabled: route.cache_enabled, - cache_policy: (route.cache_policy || - 'url') as CacheValues['cache_policy'], + cache_policy: normalizeCachePolicyValue( + route.cache_policy, + ) as CacheValues['cache_policy'], cache_rules_text: route.cache_rule_list.join('\n'), }); }, [form, route]); const watchedEnabled = form.watch('cache_enabled'); const watchedPolicy = form.watch('cache_policy'); + const needsRules = + watchedPolicy === 'suffix' || + watchedPolicy === 'path_prefix' || + watchedPolicy === 'path_exact'; const rulesHint = watchedPolicy === 'suffix' @@ -103,7 +132,9 @@ export function CacheSection({ ? '每行一个路径前缀,例如 /assets、/static。' : watchedPolicy === 'path_exact' ? '每行一个精确路径,例如 /robots.txt。' - : '按 URL 缓存时无需额外规则。'; + : watchedPolicy === 'static' + ? '标准静态资源使用内置扩展名列表(不含 HTML),无需填写规则。' + : '所有可缓存 GET 无需额外规则(仍会绕过登录态与 Authorization)。'; const rulesPlaceholder = watchedPolicy === 'suffix' @@ -112,7 +143,7 @@ export function CacheSection({ ? '/assets\n/static' : watchedPolicy === 'path_exact' ? '/robots.txt\n/manifest.json' - : '按 URL 缓存时无需额外规则'; + : '当前策略无需额外规则'; return ( 启用站点缓存 - 系统仍会自动绕过非 GET、带 Authorization 或常见登录态 Cookie - 的请求。 + 开启后默认仅缓存标准静态扩展名(不含 HTML)。仍会自动绕过非 + GET、Authorization 与常见登录态 Cookie。 @@ -181,12 +213,17 @@ export function CacheSection({ - 按 URL 缓存 - 按后缀缓存 - 按路径前缀缓存 - 按精确路径缓存 + 标准静态资源(推荐) + 所有可缓存 GET(高级) + 自定义后缀 + 路径前缀 + 精确路径 + + 标准静态资源含 css/js/图片/字体/媒体等,默认不缓存 HTML + 与接口路径。 + )} @@ -201,7 +238,7 @@ export function CacheSection({