2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 03:07:20 +08:00

feat: 优化代理缓存设置

This commit is contained in:
2026-01-29 17:47:42 +08:00
parent f87658f175
commit 8f9a32dfb0
3 changed files with 209 additions and 8 deletions

View File

@@ -12,6 +12,38 @@ import (
"github.com/samber/lo"
)
// parseDurationToSeconds 将时长字符串转换为秒数
// 支持格式: "10s", "5m", "1h", "1d"
func parseDurationToSeconds(duration string) int {
duration = strings.TrimSpace(duration)
if duration == "" {
return 600 // 默认 10 分钟
}
// 提取数字和单位
re := regexp.MustCompile(`^(\d+)([smhd]?)$`)
matches := re.FindStringSubmatch(duration)
if matches == nil {
return 600
}
value, _ := strconv.Atoi(matches[1])
unit := matches[2]
switch unit {
case "s", "":
return value
case "m":
return value * 60
case "h":
return value * 3600
case "d":
return value * 86400
default:
return value
}
}
// proxyFilePattern 匹配代理配置文件名 (200-299)
var proxyFilePattern = regexp.MustCompile(`^(\d{3})-proxy\.conf$`)
@@ -104,7 +136,24 @@ func parseProxyFile(filePath string) (*types.Proxy, error) {
// 解析 CacheEnable
if regexp.MustCompile(`CacheEnable`).MatchString(contentStr) {
proxy.Cache = true
proxy.Cache = &types.CacheConfig{
Valid: make(map[string]string),
NoCacheConditions: []string{},
UseStale: []string{},
Methods: []string{},
}
// 解析 CacheDefaultExpire
expirePattern := regexp.MustCompile(`CacheDefaultExpire\s+(\d+)`)
if em := expirePattern.FindStringSubmatch(contentStr); em != nil {
seconds, _ := strconv.Atoi(em[1])
minutes := seconds / 60
if minutes > 0 {
proxy.Cache.Valid["any"] = fmt.Sprintf("%dm", minutes)
} else {
proxy.Cache.Valid["any"] = fmt.Sprintf("%ds", seconds)
}
}
}
// 解析 Substitute (响应内容替换)
@@ -243,10 +292,20 @@ func generateProxyConfig(proxy types.Proxy) string {
}
// Cache 配置
if proxy.Cache {
if proxy.Cache != nil {
sb.WriteString(" <IfModule mod_cache.c>\n")
sb.WriteString(fmt.Sprintf(" CacheEnable disk %s\n", location))
sb.WriteString(" CacheDefaultExpire 600\n")
// 从 Valid 中获取默认过期时间
expireSeconds := 600 // 默认 10 分钟
if len(proxy.Cache.Valid) > 0 {
// 取第一个值作为默认过期时间
for _, duration := range proxy.Cache.Valid {
expireSeconds = parseDurationToSeconds(duration)
break
}
}
sb.WriteString(fmt.Sprintf(" CacheDefaultExpire %d\n", expireSeconds))
sb.WriteString(" </IfModule>\n")
}

View File

@@ -115,7 +115,70 @@ func parseProxyFile(filePath string) (*types.Proxy, error) {
// 解析 proxy_cache
cachePattern := regexp.MustCompile(`proxy_cache\s+(\S+);`)
if cm := cachePattern.FindStringSubmatch(blockContent); cm != nil && cm[1] != "off" {
proxy.Cache = true
proxy.Cache = &types.CacheConfig{
Valid: make(map[string]string),
NoCacheConditions: []string{},
UseStale: []string{},
Methods: []string{},
}
// 解析 proxy_cache_valid
cacheValidPattern := regexp.MustCompile(`proxy_cache_valid\s+([^;]+);`)
cacheValidMatches := cacheValidPattern.FindAllStringSubmatch(blockContent, -1)
for _, cvm := range cacheValidMatches {
parts := strings.Fields(cvm[1])
if len(parts) >= 2 {
// 最后一个是时长,前面的是状态码
duration := parts[len(parts)-1]
codes := strings.Join(parts[:len(parts)-1], " ")
proxy.Cache.Valid[codes] = duration
} else if len(parts) == 1 {
// 只有时长,表示 any
proxy.Cache.Valid["any"] = parts[0]
}
}
// 解析 proxy_cache_bypass / proxy_no_cache
bypassPattern := regexp.MustCompile(`proxy_cache_bypass\s+([^;]+);`)
if bm := bypassPattern.FindStringSubmatch(blockContent); bm != nil {
proxy.Cache.NoCacheConditions = strings.Fields(bm[1])
}
// 解析 proxy_cache_use_stale
useStalePattern := regexp.MustCompile(`proxy_cache_use_stale\s+([^;]+);`)
if usm := useStalePattern.FindStringSubmatch(blockContent); usm != nil {
proxy.Cache.UseStale = strings.Fields(usm[1])
}
// 解析 proxy_cache_background_update
bgUpdatePattern := regexp.MustCompile(`proxy_cache_background_update\s+(on|off);`)
if bgm := bgUpdatePattern.FindStringSubmatch(blockContent); bgm != nil {
proxy.Cache.BackgroundUpdate = bgm[1] == "on"
}
// 解析 proxy_cache_lock
lockPattern := regexp.MustCompile(`proxy_cache_lock\s+(on|off);`)
if lm := lockPattern.FindStringSubmatch(blockContent); lm != nil {
proxy.Cache.Lock = lm[1] == "on"
}
// 解析 proxy_cache_min_uses
minUsesPattern := regexp.MustCompile(`proxy_cache_min_uses\s+(\d+);`)
if mum := minUsesPattern.FindStringSubmatch(blockContent); mum != nil {
proxy.Cache.MinUses, _ = strconv.Atoi(mum[1])
}
// 解析 proxy_cache_methods
methodsPattern := regexp.MustCompile(`proxy_cache_methods\s+([^;]+);`)
if mm := methodsPattern.FindStringSubmatch(blockContent); mm != nil {
proxy.Cache.Methods = strings.Fields(mm[1])
}
// 解析 proxy_cache_key
keyPattern := regexp.MustCompile(`proxy_cache_key\s+"?([^";]+)"?;`)
if km := keyPattern.FindStringSubmatch(blockContent); km != nil {
proxy.Cache.Key = strings.TrimSpace(km[1])
}
}
// 解析 resolver
@@ -272,10 +335,60 @@ func generateProxyConfig(proxy types.Proxy) string {
sb.WriteString(fmt.Sprintf(" proxy_buffering %s;\n", lo.If(proxy.Buffering, "on").Else("off")))
// Cache 配置
if proxy.Cache {
if proxy.Cache != nil {
sb.WriteString(" proxy_cache cache_one;\n")
sb.WriteString(" proxy_cache_valid 200 302 10m;\n")
sb.WriteString(" proxy_cache_valid 404 1m;\n")
// 缓存时长
if len(proxy.Cache.Valid) > 0 {
for codes, duration := range proxy.Cache.Valid {
if codes == "any" {
sb.WriteString(fmt.Sprintf(" proxy_cache_valid %s;\n", duration))
} else {
sb.WriteString(fmt.Sprintf(" proxy_cache_valid %s %s;\n", codes, duration))
}
}
} else {
// 默认缓存时长
sb.WriteString(" proxy_cache_valid 200 302 10m;\n")
sb.WriteString(" proxy_cache_valid 404 1m;\n")
}
// 不缓存条件
if len(proxy.Cache.NoCacheConditions) > 0 {
conditions := strings.Join(proxy.Cache.NoCacheConditions, " ")
sb.WriteString(fmt.Sprintf(" proxy_cache_bypass %s;\n", conditions))
sb.WriteString(fmt.Sprintf(" proxy_no_cache %s;\n", conditions))
}
// 过期缓存使用策略
if len(proxy.Cache.UseStale) > 0 {
sb.WriteString(fmt.Sprintf(" proxy_cache_use_stale %s;\n", strings.Join(proxy.Cache.UseStale, " ")))
}
// 后台更新
if proxy.Cache.BackgroundUpdate {
sb.WriteString(" proxy_cache_background_update on;\n")
}
// 缓存锁
if proxy.Cache.Lock {
sb.WriteString(" proxy_cache_lock on;\n")
}
// 最小请求次数
if proxy.Cache.MinUses > 0 {
sb.WriteString(fmt.Sprintf(" proxy_cache_min_uses %d;\n", proxy.Cache.MinUses))
}
// 缓存方法
if len(proxy.Cache.Methods) > 0 {
sb.WriteString(fmt.Sprintf(" proxy_cache_methods %s;\n", strings.Join(proxy.Cache.Methods, " ")))
}
// 自定义缓存键
if proxy.Cache.Key != "" {
sb.WriteString(fmt.Sprintf(" proxy_cache_key \"%s\";\n", proxy.Cache.Key))
}
}
// 自定义请求头

View File

@@ -2,13 +2,42 @@ package types
import "time"
// CacheConfig 缓存配置
type CacheConfig struct {
// 缓存时长,状态码 -> 时长,如: {"200 302": "10m", "404": "1m", "any": "5m"}
Valid map[string]string `form:"valid" json:"valid"`
// 不缓存条件 (proxy_cache_bypass + proxy_no_cache)
// 常用值: "$cookie_nocache", "$arg_nocache", "$http_pragma", "$http_authorization"
NoCacheConditions []string `form:"no_cache_conditions" json:"no_cache_conditions"`
// 过期缓存使用策略 (proxy_cache_use_stale)
// 可选值: "error", "timeout", "updating", "http_500", "http_502", "http_503", "http_504"
UseStale []string `form:"use_stale" json:"use_stale"`
// 后台更新 (proxy_cache_background_update)
BackgroundUpdate bool `form:"background_update" json:"background_update"`
// 缓存锁 (proxy_cache_lock),防止缓存击穿
Lock bool `form:"lock" json:"lock"`
// 最小请求次数 (proxy_cache_min_uses),请求 N 次后才缓存
MinUses int `form:"min_uses" json:"min_uses"`
// 缓存方法 (proxy_cache_methods),默认 GET HEAD
Methods []string `form:"methods" json:"methods"`
// 自定义缓存键 (proxy_cache_key)
Key string `form:"key" json:"key"`
}
// Proxy 反向代理配置
type Proxy struct {
Location string `form:"location" json:"location" validate:"required"` // 匹配路径,如: "/", "/api", "~ ^/api/v[0-9]+/"
Pass string `form:"pass" json:"pass" validate:"required"` // 代理地址,如: "http://example.com", "http://backend"
Host string `form:"host" json:"host"` // 代理 Host如: "example.com"
SNI string `form:"sni" json:"sni"` // 代理 SNI如: "example.com"
Cache bool `form:"cache" json:"cache"` // 是否启用缓存
Cache *CacheConfig `form:"cache" json:"cache"` // 缓存配置nil 表示禁用缓存
Buffering bool `form:"buffering" json:"buffering"` // 是否启用缓冲
Resolver []string `form:"resolver" json:"resolver"` // 自定义 DNS 解析器配置,如: ["8.8.8.8", "ipv6=off"]
ResolverTimeout time.Duration `form:"resolver_timeout" json:"resolver_timeout"` // DNS 解析超时时间,如: 5 * time.Second