From 05695f26488e423a9a7636e8d8ef74b88ae33644 Mon Sep 17 00:00:00 2001 From: engigu Date: Thu, 15 Jan 2026 20:47:42 +0800 Subject: [PATCH] feat: opt static cache --- docs/guide/url-prefix.md | 147 +++++++++++---------------------------- middleware/static.go | 53 +++++++++++++- 2 files changed, 91 insertions(+), 109 deletions(-) diff --git a/docs/guide/url-prefix.md b/docs/guide/url-prefix.md index 933aab8..703faca 100644 --- a/docs/guide/url-prefix.md +++ b/docs/guide/url-prefix.md @@ -1,77 +1,66 @@ -# URL 前缀配置 +# URL 路径前缀配置 -Message Nest 支持配置 URL 前缀,允许您将应用部署在子路径下(例如 `/message-nest`),而不是根路径。 - -## 工作原理 - -配置 URL 前缀后: -1. 后端会在所有路由前添加指定的前缀 -2. 前端页面加载时,后端会在 HTML 中自动注入前缀配置 -3. 前端的所有 API 请求会自动使用该前缀 - -**无需手动配置前端**,前后端会自动同步! - -## 使用场景 - -- 在同一域名下部署多个应用 -- 通过反向代理(如 Nginx)将应用映射到子路径 -- 在企业内网中统一管理多个服务 +Message-Nest 支持配置 URL 路径前缀,允许您在子路径下部署应用。 ## 配置方式 -### 方式一:配置文件 +### 方式 1: 配置文件 -编辑 `conf/app.ini` 文件,在 `[server]` 部分添加: +在 `conf/app.ini` 中添加: ```ini [server] -RunMode = release -HttpPort = 8000 -UrlPrefix = /message-nest +UrlPrefix = /message ``` -### 方式二:环境变量 +### 方式 2: 环境变量 设置环境变量: ```bash -export URL_PREFIX=/message-nest +export URL_PREFIX=/message ``` -或在 Docker 中: +## 使用说明 -```bash -docker run -e URL_PREFIX=/message-nest ... +1. **配置前缀**:在配置文件或环境变量中设置 `UrlPrefix` +2. **重启服务**:修改配置后需要重启 Message-Nest 服务 +3. **访问应用**:使用新的 URL 访问,例如:`http://your-domain.com/message` + +## 示例 + +### 默认访问(无前缀) +``` +http://localhost:8000/ +http://localhost:8000/api/v1/sendways/list ``` -## 配置说明 +### 配置前缀后 +```ini +[server] +UrlPrefix = /message +``` -- **路径格式**:可以带或不带前导斜杠 `/`,系统会自动处理 -- **示例值**: - - `/message-nest` - - `/api/message-nest` - - `message-nest`(会自动转换为 `/message-nest`) -- **默认值**:空字符串(部署在根路径) +访问地址变为: +``` +http://localhost:8000/message/ +http://localhost:8000/message/api/v1/sendways/list +``` -## 访问方式 +## 注意事项 -配置 URL 前缀后,访问地址会变为: +- 前缀会自动添加 `/` 前缀(如果没有的话) +- 前端静态资源会自动使用相对路径,无需额外配置 +- API 请求会自动添加路径前缀 +- 修改配置后必须重启服务才能生效 -- **原地址**:`http://your-domain.com/` -- **新地址**:`http://your-domain.com/message-nest/` - -所有 API 端点也会自动添加前缀: - -- **原 API**:`http://your-domain.com/api/v1/sendways/list` -- **新 API**:`http://your-domain.com/message-nest/api/v1/sendways/list` - -## Nginx 反向代理配置示例 +## Nginx 反向代理示例 如果使用 Nginx 反向代理,配置示例: ```nginx -location /message-nest/ { - proxy_pass http://localhost:8000/message-nest/; +location /message/ { + proxy_pass http://localhost:8000/message/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -79,63 +68,9 @@ location /message-nest/ { } ``` -## 注意事项 +## 技术实现 -::: warning 注意 -1. **修改配置后需要重启服务**才能生效 -2. **前后端会自动同步**前缀配置,后端会在 HTML 中注入配置 -3. **已有的 API 调用**会自动适配新的前缀 -4. **静态资源**(CSS、JS、图片等)也会自动使用新的前缀 -5. **开发模式**下,如果前端单独运行,需要确保后端 API 的前缀配置正确 -::: - -## 常见问题 - -### Q: 修改前缀后无法访问? - -**A:** 请检查: -1. 是否已重启服务 -2. 前缀格式是否正确 -3. 如果使用反向代理,检查代理配置是否正确 - -### Q: 可以使用多级路径吗? - -**A:** 可以,例如:`/api/message-nest` 或 `/services/messaging/nest` - -### Q: URL 前缀会影响性能吗? - -**A:** 不会,URL 前缀只是路径的一部分,不会影响应用性能。 - -## 示例 - -### 示例 1:部署在子路径 - -```ini -# conf/app.ini -[server] -UrlPrefix = /message-nest -``` - -访问地址:`http://your-domain.com/message-nest/` - -### 示例 2:多级子路径 - -```ini -# conf/app.ini -[server] -UrlPrefix = /api/services/message-nest -``` - -访问地址:`http://your-domain.com/api/services/message-nest/` - -### 示例 3:Docker 环境变量 - -```bash -docker run -d \ - -p 8000:8000 \ - -e URL_PREFIX=/message-nest \ - -e DB_TYPE=sqlite \ - message-nest:latest -``` - -访问地址:`http://localhost:8000/message-nest/` +- 后端使用 Gin 的路由组(RouterGroup)实现路径前缀 +- 前端使用 Vite 的 `base: './'` 配置生成相对路径 +- 后端在 HTML 中注入 `` 标签和配置脚本 +- 前端 API 请求自动添加路径前缀 diff --git a/middleware/static.go b/middleware/static.go index 4c6bfc7..95a86ad 100644 --- a/middleware/static.go +++ b/middleware/static.go @@ -3,16 +3,63 @@ package middleware import ( "github.com/gin-gonic/gin" "strings" + "time" ) // StaticCacheMiddleware add embed file static cache func StaticCacheMiddleware() gin.HandlerFunc { return func(c *gin.Context) { - // Apply the Cache-Control header to the static files - if strings.HasPrefix(c.Request.URL.Path, "/assets/") { - c.Header("Cache-Control", "private, max-age=86400") + path := c.Request.URL.Path + + // 对静态资源应用缓存策略 + if strings.HasPrefix(path, "/assets/") || strings.Contains(path, "/assets/") { + // 检查文件类型 + if isVersionedAsset(path) { + // 带版本号的资源(如 index-Bw4BKttg.js)可以长期缓存 + // 使用 immutable 指令,浏览器在缓存期内不会重新验证 + c.Header("Cache-Control", "public, max-age=31536000, immutable") + } else { + // 其他静态资源使用较短的缓存时间 + c.Header("Cache-Control", "public, max-age=86400") + } + + // 添加 ETag 支持 + c.Header("ETag", `"`+path+`"`) + + // 添加 Expires 头(兼容旧浏览器) + expires := time.Now().Add(24 * time.Hour).UTC().Format(time.RFC1123) + c.Header("Expires", expires) + + // 添加 Vary 头,告诉缓存服务器根据这些头部区分缓存 + c.Header("Vary", "Accept-Encoding") + } else if path == "/" || strings.HasSuffix(path, ".html") { + // HTML 文件不缓存或使用协商缓存 + c.Header("Cache-Control", "no-cache, must-revalidate") + c.Header("Pragma", "no-cache") + c.Header("Expires", "0") } + // Continue to the next middleware or handler c.Next() } } + +// isVersionedAsset 检查是否是带版本号的资源文件 +// Vite 构建的文件通常包含 hash,如:index-Bw4BKttg.js +func isVersionedAsset(path string) bool { + // 检查是否包含 hash 模式(通常是 -[hash].js 或 -[hash].css) + return strings.Contains(path, "-") && + (strings.HasSuffix(path, ".js") || + strings.HasSuffix(path, ".css") || + strings.HasSuffix(path, ".woff") || + strings.HasSuffix(path, ".woff2") || + strings.HasSuffix(path, ".ttf") || + strings.HasSuffix(path, ".eot") || + strings.HasSuffix(path, ".svg") || + strings.HasSuffix(path, ".png") || + strings.HasSuffix(path, ".jpg") || + strings.HasSuffix(path, ".jpeg") || + strings.HasSuffix(path, ".gif") || + strings.HasSuffix(path, ".webp") || + strings.HasSuffix(path, ".ico")) +}