feat: opt static cache

This commit is contained in:
engigu
2026-01-15 20:47:42 +08:00
parent 873c0bed8e
commit 05695f2648
2 changed files with 91 additions and 109 deletions
+41 -106
View File
@@ -1,77 +1,66 @@
# URL 前缀配置 # URL 路径前缀配置
Message Nest 支持配置 URL 前缀,允许您将应用部署在子路径下(例如 `/message-nest`),而不是根路径 Message-Nest 支持配置 URL 路径前缀,允许您在子路径下部署应用
## 工作原理
配置 URL 前缀后:
1. 后端会在所有路由前添加指定的前缀
2. 前端页面加载时,后端会在 HTML 中自动注入前缀配置
3. 前端的所有 API 请求会自动使用该前缀
**无需手动配置前端**,前后端会自动同步!
## 使用场景
- 在同一域名下部署多个应用
- 通过反向代理(如 Nginx)将应用映射到子路径
- 在企业内网中统一管理多个服务
## 配置方式 ## 配置方式
### 方式一:配置文件 ### 方式 1: 配置文件
编辑 `conf/app.ini` 文件,在 `[server]` 部分添加: `conf/app.ini` 添加:
```ini ```ini
[server] [server]
RunMode = release UrlPrefix = /message
HttpPort = 8000
UrlPrefix = /message-nest
``` ```
### 方式二:环境变量 ### 方式 2: 环境变量
设置环境变量: 设置环境变量:
```bash ```bash
export URL_PREFIX=/message-nest export URL_PREFIX=/message
``` ```
或在 Docker 中: ## 使用说明
```bash 1. **配置前缀**:在配置文件或环境变量中设置 `UrlPrefix`
docker run -e URL_PREFIX=/message-nest ... 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` http://localhost:8000/message/
- `/api/message-nest` http://localhost:8000/message/api/v1/sendways/list
- `message-nest`(会自动转换为 `/message-nest` ```
- **默认值**:空字符串(部署在根路径)
## 访问方式 ## 注意事项
配置 URL 前缀后,访问地址会变为: - 前缀会自动添加 `/` 前缀(如果没有的话)
- 前端静态资源会自动使用相对路径,无需额外配置
- API 请求会自动添加路径前缀
- 修改配置后必须重启服务才能生效
- **原地址**`http://your-domain.com/` ## Nginx 反向代理示例
- **新地址**`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 ```nginx
location /message-nest/ { location /message/ {
proxy_pass http://localhost:8000/message-nest/; proxy_pass http://localhost:8000/message/;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@@ -79,63 +68,9 @@ location /message-nest/ {
} }
``` ```
## 注意事项 ## 技术实现
::: warning 注意 - 后端使用 Gin 的路由组(RouterGroup)实现路径前缀
1. **修改配置后需要重启服务**才能生效 - 前端使用 Vite 的 `base: './'` 配置生成相对路径
2. **前后端会自动同步**前缀配置,后端在 HTML 中注入配置 - 后端在 HTML 中注入 `<base>` 标签和配置脚本
3. **已有的 API 调用**会自动适配新的前缀 - 前端 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/`
### 示例 3Docker 环境变量
```bash
docker run -d \
-p 8000:8000 \
-e URL_PREFIX=/message-nest \
-e DB_TYPE=sqlite \
message-nest:latest
```
访问地址:`http://localhost:8000/message-nest/`
+50 -3
View File
@@ -3,16 +3,63 @@ package middleware
import ( import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"strings" "strings"
"time"
) )
// StaticCacheMiddleware add embed file static cache // StaticCacheMiddleware add embed file static cache
func StaticCacheMiddleware() gin.HandlerFunc { func StaticCacheMiddleware() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
// Apply the Cache-Control header to the static files path := c.Request.URL.Path
if strings.HasPrefix(c.Request.URL.Path, "/assets/") {
c.Header("Cache-Control", "private, max-age=86400") // 对静态资源应用缓存策略
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 // Continue to the next middleware or handler
c.Next() 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"))
}