|
8 | 8 | "log" |
9 | 9 | "net/http" |
10 | 10 | "os" |
| 11 | + "strings" |
11 | 12 |
|
12 | 13 | "hook-panel/internal/handlers" |
13 | 14 | "hook-panel/internal/middleware" |
@@ -61,6 +62,9 @@ func setupStaticFiles(r *gin.Engine) { |
61 | 62 | contentType := getContentType(filePath) |
62 | 63 | c.Header("Content-Type", contentType) |
63 | 64 |
|
| 65 | + // 设置缓存头 |
| 66 | + setCacheHeaders(c, filePath) |
| 67 | + |
64 | 68 | if stat, err := file.Stat(); err == nil { |
65 | 69 | c.DataFromReader(http.StatusOK, stat.Size(), contentType, file, nil) |
66 | 70 | return |
@@ -124,6 +128,73 @@ func getContentType(filePath string) string { |
124 | 128 | } |
125 | 129 | } |
126 | 130 |
|
| 131 | +// setCacheHeaders 根据文件类型设置缓存策略 |
| 132 | +func setCacheHeaders(c *gin.Context, filePath string) { |
| 133 | + // HTML 文件不缓存,确保用户总是获取最新页面 |
| 134 | + if strings.HasSuffix(filePath, ".html") { |
| 135 | + c.Header("Cache-Control", "no-cache, no-store, must-revalidate") |
| 136 | + c.Header("Pragma", "no-cache") |
| 137 | + c.Header("Expires", "0") |
| 138 | + return |
| 139 | + } |
| 140 | + |
| 141 | + // 检查是否是带 hash 的资源文件(通常包含 8 位以上的十六进制字符) |
| 142 | + // 例如: app.a1b2c3d4.js, style.12345678.css |
| 143 | + isHashedFile := false |
| 144 | + fileName := filePath |
| 145 | + if lastSlash := strings.LastIndex(filePath, "/"); lastSlash != -1 { |
| 146 | + fileName = filePath[lastSlash+1:] |
| 147 | + } |
| 148 | + |
| 149 | + // 简单判断:如果文件名中包含类似 hash 的模式 |
| 150 | + if strings.Contains(fileName, ".") { |
| 151 | + parts := strings.Split(fileName, ".") |
| 152 | + for i := 1; i < len(parts)-1; i++ { |
| 153 | + if len(parts[i]) >= 8 && isHexString(parts[i]) { |
| 154 | + isHashedFile = true |
| 155 | + break |
| 156 | + } |
| 157 | + } |
| 158 | + } |
| 159 | + |
| 160 | + // 对于带 hash 的资源文件,设置长期缓存(1年) |
| 161 | + if isHashedFile { |
| 162 | + c.Header("Cache-Control", "public, max-age=31536000, immutable") |
| 163 | + return |
| 164 | + } |
| 165 | + |
| 166 | + // 对于 JS、CSS 等静态资源,设置较短的缓存(1小时) |
| 167 | + if strings.HasSuffix(filePath, ".js") || strings.HasSuffix(filePath, ".css") { |
| 168 | + c.Header("Cache-Control", "public, max-age=3600") |
| 169 | + return |
| 170 | + } |
| 171 | + |
| 172 | + // 对于图片等静态资源,设置中等缓存(1天) |
| 173 | + if strings.HasSuffix(filePath, ".png") || strings.HasSuffix(filePath, ".jpg") || |
| 174 | + strings.HasSuffix(filePath, ".jpeg") || strings.HasSuffix(filePath, ".gif") || |
| 175 | + strings.HasSuffix(filePath, ".svg") || strings.HasSuffix(filePath, ".ico") || |
| 176 | + strings.HasSuffix(filePath, ".webp") { |
| 177 | + c.Header("Cache-Control", "public, max-age=86400") |
| 178 | + return |
| 179 | + } |
| 180 | + |
| 181 | + // 其他文件默认缓存1小时 |
| 182 | + c.Header("Cache-Control", "public, max-age=3600") |
| 183 | +} |
| 184 | + |
| 185 | +// isHexString 检查字符串是否为十六进制 |
| 186 | +func isHexString(s string) bool { |
| 187 | + if len(s) == 0 { |
| 188 | + return false |
| 189 | + } |
| 190 | + for _, c := range s { |
| 191 | + if !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { |
| 192 | + return false |
| 193 | + } |
| 194 | + } |
| 195 | + return true |
| 196 | +} |
| 197 | + |
127 | 198 | func main() { |
128 | 199 | // 解析命令行参数 |
129 | 200 | var port string |
|
0 commit comments